1 // Copyright 2011 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 //
28 // Tests for heap profiler
29
30 #include <ctype.h>
31
32 #include "src/v8.h"
33
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 "src/snapshot.h"
40 #include "src/utils-inl.h"
41 #include "test/cctest/cctest.h"
42
43 using i::AllocationTraceNode;
44 using i::AllocationTraceTree;
45 using i::AllocationTracker;
46 using i::HashMap;
47 using i::Vector;
48
49 namespace {
50
51 class NamedEntriesDetector {
52 public:
NamedEntriesDetector()53 NamedEntriesDetector()
54 : has_A2(false), has_B2(false), has_C2(false) {
55 }
56
CheckEntry(i::HeapEntry * entry)57 void CheckEntry(i::HeapEntry* entry) {
58 if (strcmp(entry->name(), "A2") == 0) has_A2 = true;
59 if (strcmp(entry->name(), "B2") == 0) has_B2 = true;
60 if (strcmp(entry->name(), "C2") == 0) has_C2 = true;
61 }
62
AddressesMatch(void * key1,void * key2)63 static bool AddressesMatch(void* key1, void* key2) {
64 return key1 == key2;
65 }
66
CheckAllReachables(i::HeapEntry * root)67 void CheckAllReachables(i::HeapEntry* root) {
68 i::HashMap visited(AddressesMatch);
69 i::List<i::HeapEntry*> list(10);
70 list.Add(root);
71 CheckEntry(root);
72 while (!list.is_empty()) {
73 i::HeapEntry* entry = list.RemoveLast();
74 i::Vector<i::HeapGraphEdge*> children = entry->children();
75 for (int i = 0; i < children.length(); ++i) {
76 if (children[i]->type() == i::HeapGraphEdge::kShortcut) continue;
77 i::HeapEntry* child = children[i]->to();
78 i::HashMap::Entry* entry = visited.Lookup(
79 reinterpret_cast<void*>(child),
80 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(child)),
81 true);
82 if (entry->value)
83 continue;
84 entry->value = reinterpret_cast<void*>(1);
85 list.Add(child);
86 CheckEntry(child);
87 }
88 }
89 }
90
91 bool has_A2;
92 bool has_B2;
93 bool has_C2;
94 };
95
96 } // namespace
97
98
GetGlobalObject(const v8::HeapSnapshot * snapshot)99 static const v8::HeapGraphNode* GetGlobalObject(
100 const v8::HeapSnapshot* snapshot) {
101 CHECK_EQ(2, snapshot->GetRoot()->GetChildrenCount());
102 // The 0th-child is (GC Roots), 1st is the user root.
103 const v8::HeapGraphNode* global_obj =
104 snapshot->GetRoot()->GetChild(1)->GetToNode();
105 CHECK_EQ(0, strncmp("Object", const_cast<i::HeapEntry*>(
106 reinterpret_cast<const i::HeapEntry*>(global_obj))->name(), 6));
107 return global_obj;
108 }
109
110
GetProperty(const v8::HeapGraphNode * node,v8::HeapGraphEdge::Type type,const char * name)111 static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node,
112 v8::HeapGraphEdge::Type type,
113 const char* name) {
114 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
115 const v8::HeapGraphEdge* prop = node->GetChild(i);
116 v8::String::Utf8Value prop_name(prop->GetName());
117 if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
118 return prop->GetToNode();
119 }
120 return NULL;
121 }
122
123
HasString(const v8::HeapGraphNode * node,const char * contents)124 static bool HasString(const v8::HeapGraphNode* node, const char* contents) {
125 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
126 const v8::HeapGraphEdge* prop = node->GetChild(i);
127 const v8::HeapGraphNode* node = prop->GetToNode();
128 if (node->GetType() == v8::HeapGraphNode::kString) {
129 v8::String::Utf8Value node_name(node->GetName());
130 if (strcmp(contents, *node_name) == 0) return true;
131 }
132 }
133 return false;
134 }
135
136
AddressesMatch(void * key1,void * key2)137 static bool AddressesMatch(void* key1, void* key2) {
138 return key1 == key2;
139 }
140
141
142 // Check that snapshot has no unretained entries except root.
ValidateSnapshot(const v8::HeapSnapshot * snapshot,int depth=3)143 static bool ValidateSnapshot(const v8::HeapSnapshot* snapshot, int depth = 3) {
144 i::HeapSnapshot* heap_snapshot = const_cast<i::HeapSnapshot*>(
145 reinterpret_cast<const i::HeapSnapshot*>(snapshot));
146
147 i::HashMap visited(AddressesMatch);
148 i::List<i::HeapGraphEdge>& edges = heap_snapshot->edges();
149 for (int i = 0; i < edges.length(); ++i) {
150 i::HashMap::Entry* entry = visited.Lookup(
151 reinterpret_cast<void*>(edges[i].to()),
152 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(edges[i].to())),
153 true);
154 uint32_t ref_count = static_cast<uint32_t>(
155 reinterpret_cast<uintptr_t>(entry->value));
156 entry->value = reinterpret_cast<void*>(ref_count + 1);
157 }
158 uint32_t unretained_entries_count = 0;
159 i::List<i::HeapEntry>& entries = heap_snapshot->entries();
160 for (int i = 0; i < entries.length(); ++i) {
161 i::HashMap::Entry* entry = visited.Lookup(
162 reinterpret_cast<void*>(&entries[i]),
163 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&entries[i])),
164 false);
165 if (!entry && entries[i].id() != 1) {
166 entries[i].Print("entry with no retainer", "", depth, 0);
167 ++unretained_entries_count;
168 }
169 }
170 return unretained_entries_count == 0;
171 }
172
173
TEST(HeapSnapshot)174 TEST(HeapSnapshot) {
175 LocalContext env2;
176 v8::HandleScope scope(env2->GetIsolate());
177 v8::HeapProfiler* heap_profiler = env2->GetIsolate()->GetHeapProfiler();
178
179 CompileRun(
180 "function A2() {}\n"
181 "function B2(x) { return function() { return typeof x; }; }\n"
182 "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n"
183 "var a2 = new A2();\n"
184 "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n"
185 "var c2 = new C2(a2);");
186 const v8::HeapSnapshot* snapshot_env2 =
187 heap_profiler->TakeHeapSnapshot(v8_str("env2"));
188 CHECK(ValidateSnapshot(snapshot_env2));
189 const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2);
190
191 // Verify, that JS global object of env2 has '..2' properties.
192 const v8::HeapGraphNode* a2_node =
193 GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "a2");
194 CHECK_NE(NULL, a2_node);
195 CHECK_NE(
196 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_1"));
197 CHECK_NE(
198 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_2"));
199 CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "c2"));
200
201 NamedEntriesDetector det;
202 det.CheckAllReachables(const_cast<i::HeapEntry*>(
203 reinterpret_cast<const i::HeapEntry*>(global_env2)));
204 CHECK(det.has_A2);
205 CHECK(det.has_B2);
206 CHECK(det.has_C2);
207 }
208
209
TEST(HeapSnapshotObjectSizes)210 TEST(HeapSnapshotObjectSizes) {
211 LocalContext env;
212 v8::HandleScope scope(env->GetIsolate());
213 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
214
215 // -a-> X1 --a
216 // x -b-> X2 <-|
217 CompileRun(
218 "function X(a, b) { this.a = a; this.b = b; }\n"
219 "x = new X(new X(), new X());\n"
220 "dummy = new X();\n"
221 "(function() { x.a.a = x.b; })();");
222 const v8::HeapSnapshot* snapshot =
223 heap_profiler->TakeHeapSnapshot(v8_str("sizes"));
224 CHECK(ValidateSnapshot(snapshot));
225 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
226 const v8::HeapGraphNode* x =
227 GetProperty(global, v8::HeapGraphEdge::kProperty, "x");
228 CHECK_NE(NULL, x);
229 const v8::HeapGraphNode* x1 =
230 GetProperty(x, v8::HeapGraphEdge::kProperty, "a");
231 CHECK_NE(NULL, x1);
232 const v8::HeapGraphNode* x2 =
233 GetProperty(x, v8::HeapGraphEdge::kProperty, "b");
234 CHECK_NE(NULL, x2);
235
236 // Test sizes.
237 CHECK_NE(0, static_cast<int>(x->GetShallowSize()));
238 CHECK_NE(0, static_cast<int>(x1->GetShallowSize()));
239 CHECK_NE(0, static_cast<int>(x2->GetShallowSize()));
240 }
241
242
TEST(BoundFunctionInSnapshot)243 TEST(BoundFunctionInSnapshot) {
244 LocalContext env;
245 v8::HandleScope scope(env->GetIsolate());
246 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
247 CompileRun(
248 "function myFunction(a, b) { this.a = a; this.b = b; }\n"
249 "function AAAAA() {}\n"
250 "boundFunction = myFunction.bind(new AAAAA(), 20, new Number(12)); \n");
251 const v8::HeapSnapshot* snapshot =
252 heap_profiler->TakeHeapSnapshot(v8_str("sizes"));
253 CHECK(ValidateSnapshot(snapshot));
254 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
255 const v8::HeapGraphNode* f =
256 GetProperty(global, v8::HeapGraphEdge::kProperty, "boundFunction");
257 CHECK(f);
258 CHECK_EQ(v8::String::NewFromUtf8(env->GetIsolate(), "native_bind"),
259 f->GetName());
260 const v8::HeapGraphNode* bindings =
261 GetProperty(f, v8::HeapGraphEdge::kInternal, "bindings");
262 CHECK_NE(NULL, bindings);
263 CHECK_EQ(v8::HeapGraphNode::kArray, bindings->GetType());
264 CHECK_EQ(4, bindings->GetChildrenCount());
265
266 const v8::HeapGraphNode* bound_this = GetProperty(
267 f, v8::HeapGraphEdge::kShortcut, "bound_this");
268 CHECK(bound_this);
269 CHECK_EQ(v8::HeapGraphNode::kObject, bound_this->GetType());
270
271 const v8::HeapGraphNode* bound_function = GetProperty(
272 f, v8::HeapGraphEdge::kShortcut, "bound_function");
273 CHECK(bound_function);
274 CHECK_EQ(v8::HeapGraphNode::kClosure, bound_function->GetType());
275
276 const v8::HeapGraphNode* bound_argument = GetProperty(
277 f, v8::HeapGraphEdge::kShortcut, "bound_argument_1");
278 CHECK(bound_argument);
279 CHECK_EQ(v8::HeapGraphNode::kObject, bound_argument->GetType());
280 }
281
282
TEST(HeapSnapshotEntryChildren)283 TEST(HeapSnapshotEntryChildren) {
284 LocalContext env;
285 v8::HandleScope scope(env->GetIsolate());
286 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
287
288 CompileRun(
289 "function A() { }\n"
290 "a = new A;");
291 const v8::HeapSnapshot* snapshot =
292 heap_profiler->TakeHeapSnapshot(v8_str("children"));
293 CHECK(ValidateSnapshot(snapshot));
294 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
295 for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) {
296 const v8::HeapGraphEdge* prop = global->GetChild(i);
297 CHECK_EQ(global, prop->GetFromNode());
298 }
299 const v8::HeapGraphNode* a =
300 GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
301 CHECK_NE(NULL, a);
302 for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) {
303 const v8::HeapGraphEdge* prop = a->GetChild(i);
304 CHECK_EQ(a, prop->GetFromNode());
305 }
306 }
307
308
TEST(HeapSnapshotCodeObjects)309 TEST(HeapSnapshotCodeObjects) {
310 LocalContext env;
311 v8::HandleScope scope(env->GetIsolate());
312 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
313
314 CompileRun(
315 "function lazy(x) { return x - 1; }\n"
316 "function compiled(x) { return x + 1; }\n"
317 "var anonymous = (function() { return function() { return 0; } })();\n"
318 "compiled(1)");
319 const v8::HeapSnapshot* snapshot =
320 heap_profiler->TakeHeapSnapshot(v8_str("code"));
321 CHECK(ValidateSnapshot(snapshot));
322
323 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
324 const v8::HeapGraphNode* compiled =
325 GetProperty(global, v8::HeapGraphEdge::kProperty, "compiled");
326 CHECK_NE(NULL, compiled);
327 CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType());
328 const v8::HeapGraphNode* lazy =
329 GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy");
330 CHECK_NE(NULL, lazy);
331 CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
332 const v8::HeapGraphNode* anonymous =
333 GetProperty(global, v8::HeapGraphEdge::kProperty, "anonymous");
334 CHECK_NE(NULL, anonymous);
335 CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
336 v8::String::Utf8Value anonymous_name(anonymous->GetName());
337 CHECK_EQ("", *anonymous_name);
338
339 // Find references to code.
340 const v8::HeapGraphNode* compiled_code =
341 GetProperty(compiled, v8::HeapGraphEdge::kInternal, "shared");
342 CHECK_NE(NULL, compiled_code);
343 const v8::HeapGraphNode* lazy_code =
344 GetProperty(lazy, v8::HeapGraphEdge::kInternal, "shared");
345 CHECK_NE(NULL, lazy_code);
346
347 // Check that there's no strong next_code_link. There might be a weak one
348 // but might be not, so we can't check that fact.
349 const v8::HeapGraphNode* code =
350 GetProperty(compiled_code, v8::HeapGraphEdge::kInternal, "code");
351 CHECK_NE(NULL, code);
352 const v8::HeapGraphNode* next_code_link =
353 GetProperty(code, v8::HeapGraphEdge::kInternal, "code");
354 CHECK_EQ(NULL, next_code_link);
355
356 // Verify that non-compiled code doesn't contain references to "x"
357 // literal, while compiled code does. The scope info is stored in FixedArray
358 // objects attached to the SharedFunctionInfo.
359 bool compiled_references_x = false, lazy_references_x = false;
360 for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) {
361 const v8::HeapGraphEdge* prop = compiled_code->GetChild(i);
362 const v8::HeapGraphNode* node = prop->GetToNode();
363 if (node->GetType() == v8::HeapGraphNode::kArray) {
364 if (HasString(node, "x")) {
365 compiled_references_x = true;
366 break;
367 }
368 }
369 }
370 for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) {
371 const v8::HeapGraphEdge* prop = lazy_code->GetChild(i);
372 const v8::HeapGraphNode* node = prop->GetToNode();
373 if (node->GetType() == v8::HeapGraphNode::kArray) {
374 if (HasString(node, "x")) {
375 lazy_references_x = true;
376 break;
377 }
378 }
379 }
380 CHECK(compiled_references_x);
381 CHECK(!lazy_references_x);
382 }
383
384
TEST(HeapSnapshotHeapNumbers)385 TEST(HeapSnapshotHeapNumbers) {
386 LocalContext env;
387 v8::HandleScope scope(env->GetIsolate());
388 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
389 CompileRun(
390 "a = 1; // a is Smi\n"
391 "b = 2.5; // b is HeapNumber");
392 const v8::HeapSnapshot* snapshot =
393 heap_profiler->TakeHeapSnapshot(v8_str("numbers"));
394 CHECK(ValidateSnapshot(snapshot));
395 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
396 CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kProperty, "a"));
397 const v8::HeapGraphNode* b =
398 GetProperty(global, v8::HeapGraphEdge::kProperty, "b");
399 CHECK_NE(NULL, b);
400 CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType());
401 }
402
403
TEST(HeapSnapshotSlicedString)404 TEST(HeapSnapshotSlicedString) {
405 LocalContext env;
406 v8::HandleScope scope(env->GetIsolate());
407 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
408 CompileRun(
409 "parent_string = \"123456789.123456789.123456789.123456789.123456789."
410 "123456789.123456789.123456789.123456789.123456789."
411 "123456789.123456789.123456789.123456789.123456789."
412 "123456789.123456789.123456789.123456789.123456789.\";"
413 "child_string = parent_string.slice(100);");
414 const v8::HeapSnapshot* snapshot =
415 heap_profiler->TakeHeapSnapshot(v8_str("strings"));
416 CHECK(ValidateSnapshot(snapshot));
417 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
418 const v8::HeapGraphNode* parent_string =
419 GetProperty(global, v8::HeapGraphEdge::kProperty, "parent_string");
420 CHECK_NE(NULL, parent_string);
421 const v8::HeapGraphNode* child_string =
422 GetProperty(global, v8::HeapGraphEdge::kProperty, "child_string");
423 CHECK_NE(NULL, child_string);
424 CHECK_EQ(v8::HeapGraphNode::kSlicedString, child_string->GetType());
425 const v8::HeapGraphNode* parent =
426 GetProperty(child_string, v8::HeapGraphEdge::kInternal, "parent");
427 CHECK_EQ(parent_string, parent);
428 heap_profiler->DeleteAllHeapSnapshots();
429 }
430
431
TEST(HeapSnapshotConsString)432 TEST(HeapSnapshotConsString) {
433 v8::Isolate* isolate = CcTest::isolate();
434 v8::HandleScope scope(isolate);
435 v8::Local<v8::ObjectTemplate> global_template =
436 v8::ObjectTemplate::New(isolate);
437 global_template->SetInternalFieldCount(1);
438 LocalContext env(NULL, global_template);
439 v8::Handle<v8::Object> global_proxy = env->Global();
440 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
441 CHECK_EQ(1, global->InternalFieldCount());
442
443 i::Factory* factory = CcTest::i_isolate()->factory();
444 i::Handle<i::String> first = factory->NewStringFromStaticChars("0123456789");
445 i::Handle<i::String> second = factory->NewStringFromStaticChars("0123456789");
446 i::Handle<i::String> cons_string =
447 factory->NewConsString(first, second).ToHandleChecked();
448
449 global->SetInternalField(0, v8::ToApiHandle<v8::String>(cons_string));
450
451 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
452 const v8::HeapSnapshot* snapshot =
453 heap_profiler->TakeHeapSnapshot(v8_str("cons_strings"));
454 CHECK(ValidateSnapshot(snapshot));
455 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
456
457 const v8::HeapGraphNode* string_node =
458 GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0");
459 CHECK_NE(NULL, string_node);
460 CHECK_EQ(v8::HeapGraphNode::kConsString, string_node->GetType());
461
462 const v8::HeapGraphNode* first_node =
463 GetProperty(string_node, v8::HeapGraphEdge::kInternal, "first");
464 CHECK_EQ(v8::HeapGraphNode::kString, first_node->GetType());
465
466 const v8::HeapGraphNode* second_node =
467 GetProperty(string_node, v8::HeapGraphEdge::kInternal, "second");
468 CHECK_EQ(v8::HeapGraphNode::kString, second_node->GetType());
469
470 heap_profiler->DeleteAllHeapSnapshots();
471 }
472
473
TEST(HeapSnapshotSymbol)474 TEST(HeapSnapshotSymbol) {
475 LocalContext env;
476 v8::HandleScope scope(env->GetIsolate());
477 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
478
479 CompileRun("a = Symbol('mySymbol');\n");
480 const v8::HeapSnapshot* snapshot =
481 heap_profiler->TakeHeapSnapshot(v8_str("Symbol"));
482 CHECK(ValidateSnapshot(snapshot));
483 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
484 const v8::HeapGraphNode* a =
485 GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
486 CHECK_NE(NULL, a);
487 CHECK_EQ(a->GetType(), v8::HeapGraphNode::kSymbol);
488 CHECK_EQ(v8_str("symbol"), a->GetName());
489 const v8::HeapGraphNode* name =
490 GetProperty(a, v8::HeapGraphEdge::kInternal, "name");
491 CHECK_NE(NULL, name);
492 CHECK_EQ(v8_str("mySymbol"), name->GetName());
493 }
494
495
TEST(HeapSnapshotWeakCollection)496 TEST(HeapSnapshotWeakCollection) {
497 LocalContext env;
498 v8::HandleScope scope(env->GetIsolate());
499 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
500
501 CompileRun(
502 "k = {}; v = {}; s = 'str';\n"
503 "ws = new WeakSet(); ws.add(k); ws.add(v); ws[s] = s;\n"
504 "wm = new WeakMap(); wm.set(k, v); wm[s] = s;\n");
505 const v8::HeapSnapshot* snapshot =
506 heap_profiler->TakeHeapSnapshot(v8_str("WeakCollections"));
507 CHECK(ValidateSnapshot(snapshot));
508 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
509 const v8::HeapGraphNode* k =
510 GetProperty(global, v8::HeapGraphEdge::kProperty, "k");
511 CHECK_NE(NULL, k);
512 const v8::HeapGraphNode* v =
513 GetProperty(global, v8::HeapGraphEdge::kProperty, "v");
514 CHECK_NE(NULL, v);
515 const v8::HeapGraphNode* s =
516 GetProperty(global, v8::HeapGraphEdge::kProperty, "s");
517 CHECK_NE(NULL, s);
518
519 const v8::HeapGraphNode* ws =
520 GetProperty(global, v8::HeapGraphEdge::kProperty, "ws");
521 CHECK_NE(NULL, ws);
522 CHECK_EQ(v8::HeapGraphNode::kObject, ws->GetType());
523 CHECK_EQ(v8_str("WeakSet"), ws->GetName());
524
525 const v8::HeapGraphNode* ws_table =
526 GetProperty(ws, v8::HeapGraphEdge::kInternal, "table");
527 CHECK_EQ(v8::HeapGraphNode::kArray, ws_table->GetType());
528 CHECK_GT(ws_table->GetChildrenCount(), 0);
529 int weak_entries = 0;
530 for (int i = 0, count = ws_table->GetChildrenCount(); i < count; ++i) {
531 const v8::HeapGraphEdge* prop = ws_table->GetChild(i);
532 if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue;
533 if (k->GetId() == prop->GetToNode()->GetId()) {
534 ++weak_entries;
535 }
536 }
537 CHECK_EQ(1, weak_entries);
538 const v8::HeapGraphNode* ws_s =
539 GetProperty(ws, v8::HeapGraphEdge::kProperty, "str");
540 CHECK_NE(NULL, ws_s);
541 CHECK_EQ(static_cast<int>(s->GetId()), static_cast<int>(ws_s->GetId()));
542
543 const v8::HeapGraphNode* wm =
544 GetProperty(global, v8::HeapGraphEdge::kProperty, "wm");
545 CHECK_NE(NULL, wm);
546 CHECK_EQ(v8::HeapGraphNode::kObject, wm->GetType());
547 CHECK_EQ(v8_str("WeakMap"), wm->GetName());
548
549 const v8::HeapGraphNode* wm_table =
550 GetProperty(wm, v8::HeapGraphEdge::kInternal, "table");
551 CHECK_EQ(v8::HeapGraphNode::kArray, wm_table->GetType());
552 CHECK_GT(wm_table->GetChildrenCount(), 0);
553 weak_entries = 0;
554 for (int i = 0, count = wm_table->GetChildrenCount(); i < count; ++i) {
555 const v8::HeapGraphEdge* prop = wm_table->GetChild(i);
556 if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue;
557 const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
558 if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
559 ++weak_entries;
560 }
561 }
562 CHECK_EQ(2, weak_entries);
563 const v8::HeapGraphNode* wm_s =
564 GetProperty(wm, v8::HeapGraphEdge::kProperty, "str");
565 CHECK_NE(NULL, wm_s);
566 CHECK_EQ(static_cast<int>(s->GetId()), static_cast<int>(wm_s->GetId()));
567 }
568
569
TEST(HeapSnapshotCollection)570 TEST(HeapSnapshotCollection) {
571 LocalContext env;
572 v8::HandleScope scope(env->GetIsolate());
573 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
574
575 CompileRun(
576 "k = {}; v = {}; s = 'str';\n"
577 "set = new Set(); set.add(k); set.add(v); set[s] = s;\n"
578 "map = new Map(); map.set(k, v); map[s] = s;\n");
579 const v8::HeapSnapshot* snapshot =
580 heap_profiler->TakeHeapSnapshot(v8_str("Collections"));
581 CHECK(ValidateSnapshot(snapshot));
582 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
583 const v8::HeapGraphNode* k =
584 GetProperty(global, v8::HeapGraphEdge::kProperty, "k");
585 CHECK_NE(NULL, k);
586 const v8::HeapGraphNode* v =
587 GetProperty(global, v8::HeapGraphEdge::kProperty, "v");
588 CHECK_NE(NULL, v);
589 const v8::HeapGraphNode* s =
590 GetProperty(global, v8::HeapGraphEdge::kProperty, "s");
591 CHECK_NE(NULL, s);
592
593 const v8::HeapGraphNode* set =
594 GetProperty(global, v8::HeapGraphEdge::kProperty, "set");
595 CHECK_NE(NULL, set);
596 CHECK_EQ(v8::HeapGraphNode::kObject, set->GetType());
597 CHECK_EQ(v8_str("Set"), set->GetName());
598
599 const v8::HeapGraphNode* set_table =
600 GetProperty(set, v8::HeapGraphEdge::kInternal, "table");
601 CHECK_EQ(v8::HeapGraphNode::kArray, set_table->GetType());
602 CHECK_GT(set_table->GetChildrenCount(), 0);
603 int entries = 0;
604 for (int i = 0, count = set_table->GetChildrenCount(); i < count; ++i) {
605 const v8::HeapGraphEdge* prop = set_table->GetChild(i);
606 const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
607 if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
608 ++entries;
609 }
610 }
611 CHECK_EQ(2, entries);
612 const v8::HeapGraphNode* set_s =
613 GetProperty(set, v8::HeapGraphEdge::kProperty, "str");
614 CHECK_NE(NULL, set_s);
615 CHECK_EQ(static_cast<int>(s->GetId()), static_cast<int>(set_s->GetId()));
616
617 const v8::HeapGraphNode* map =
618 GetProperty(global, v8::HeapGraphEdge::kProperty, "map");
619 CHECK_NE(NULL, map);
620 CHECK_EQ(v8::HeapGraphNode::kObject, map->GetType());
621 CHECK_EQ(v8_str("Map"), map->GetName());
622
623 const v8::HeapGraphNode* map_table =
624 GetProperty(map, v8::HeapGraphEdge::kInternal, "table");
625 CHECK_EQ(v8::HeapGraphNode::kArray, map_table->GetType());
626 CHECK_GT(map_table->GetChildrenCount(), 0);
627 entries = 0;
628 for (int i = 0, count = map_table->GetChildrenCount(); i < count; ++i) {
629 const v8::HeapGraphEdge* prop = map_table->GetChild(i);
630 const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
631 if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
632 ++entries;
633 }
634 }
635 CHECK_EQ(2, entries);
636 const v8::HeapGraphNode* map_s =
637 GetProperty(map, v8::HeapGraphEdge::kProperty, "str");
638 CHECK_NE(NULL, map_s);
639 CHECK_EQ(static_cast<int>(s->GetId()), static_cast<int>(map_s->GetId()));
640 }
641
642
TEST(HeapSnapshotInternalReferences)643 TEST(HeapSnapshotInternalReferences) {
644 v8::Isolate* isolate = CcTest::isolate();
645 v8::HandleScope scope(isolate);
646 v8::Local<v8::ObjectTemplate> global_template =
647 v8::ObjectTemplate::New(isolate);
648 global_template->SetInternalFieldCount(2);
649 LocalContext env(NULL, global_template);
650 v8::Handle<v8::Object> global_proxy = env->Global();
651 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
652 CHECK_EQ(2, global->InternalFieldCount());
653 v8::Local<v8::Object> obj = v8::Object::New(isolate);
654 global->SetInternalField(0, v8_num(17));
655 global->SetInternalField(1, obj);
656 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
657 const v8::HeapSnapshot* snapshot =
658 heap_profiler->TakeHeapSnapshot(v8_str("internals"));
659 CHECK(ValidateSnapshot(snapshot));
660 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
661 // The first reference will not present, because it's a Smi.
662 CHECK_EQ(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0"));
663 // The second reference is to an object.
664 CHECK_NE(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "1"));
665 }
666
667
668 // Trying to introduce a check helper for uint32_t causes many
669 // overloading ambiguities, so it seems easier just to cast
670 // them to a signed type.
671 #define CHECK_EQ_SNAPSHOT_OBJECT_ID(a, b) \
672 CHECK_EQ(static_cast<int32_t>(a), static_cast<int32_t>(b))
673 #define CHECK_NE_SNAPSHOT_OBJECT_ID(a, b) \
674 CHECK((a) != (b)) // NOLINT
675
TEST(HeapSnapshotAddressReuse)676 TEST(HeapSnapshotAddressReuse) {
677 LocalContext env;
678 v8::HandleScope scope(env->GetIsolate());
679 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
680
681 CompileRun(
682 "function A() {}\n"
683 "var a = [];\n"
684 "for (var i = 0; i < 10000; ++i)\n"
685 " a[i] = new A();\n");
686 const v8::HeapSnapshot* snapshot1 =
687 heap_profiler->TakeHeapSnapshot(v8_str("snapshot1"));
688 CHECK(ValidateSnapshot(snapshot1));
689 v8::SnapshotObjectId maxId1 = snapshot1->GetMaxSnapshotJSObjectId();
690
691 CompileRun(
692 "for (var i = 0; i < 10000; ++i)\n"
693 " a[i] = new A();\n");
694 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
695
696 const v8::HeapSnapshot* snapshot2 =
697 heap_profiler->TakeHeapSnapshot(v8_str("snapshot2"));
698 CHECK(ValidateSnapshot(snapshot2));
699 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
700
701 const v8::HeapGraphNode* array_node =
702 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
703 CHECK_NE(NULL, array_node);
704 int wrong_count = 0;
705 for (int i = 0, count = array_node->GetChildrenCount(); i < count; ++i) {
706 const v8::HeapGraphEdge* prop = array_node->GetChild(i);
707 if (prop->GetType() != v8::HeapGraphEdge::kElement)
708 continue;
709 v8::SnapshotObjectId id = prop->GetToNode()->GetId();
710 if (id < maxId1)
711 ++wrong_count;
712 }
713 CHECK_EQ(0, wrong_count);
714 }
715
716
TEST(HeapEntryIdsAndArrayShift)717 TEST(HeapEntryIdsAndArrayShift) {
718 LocalContext env;
719 v8::HandleScope scope(env->GetIsolate());
720 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
721
722 CompileRun(
723 "function AnObject() {\n"
724 " this.first = 'first';\n"
725 " this.second = 'second';\n"
726 "}\n"
727 "var a = new Array();\n"
728 "for (var i = 0; i < 10; ++i)\n"
729 " a.push(new AnObject());\n");
730 const v8::HeapSnapshot* snapshot1 =
731 heap_profiler->TakeHeapSnapshot(v8_str("s1"));
732 CHECK(ValidateSnapshot(snapshot1));
733
734 CompileRun(
735 "for (var i = 0; i < 1; ++i)\n"
736 " a.shift();\n");
737
738 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
739
740 const v8::HeapSnapshot* snapshot2 =
741 heap_profiler->TakeHeapSnapshot(v8_str("s2"));
742 CHECK(ValidateSnapshot(snapshot2));
743
744 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
745 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
746 CHECK_NE_SNAPSHOT_OBJECT_ID(0, global1->GetId());
747 CHECK_EQ_SNAPSHOT_OBJECT_ID(global1->GetId(), global2->GetId());
748
749 const v8::HeapGraphNode* a1 =
750 GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
751 CHECK_NE(NULL, a1);
752 const v8::HeapGraphNode* k1 =
753 GetProperty(a1, v8::HeapGraphEdge::kInternal, "elements");
754 CHECK_NE(NULL, k1);
755 const v8::HeapGraphNode* a2 =
756 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
757 CHECK_NE(NULL, a2);
758 const v8::HeapGraphNode* k2 =
759 GetProperty(a2, v8::HeapGraphEdge::kInternal, "elements");
760 CHECK_NE(NULL, k2);
761
762 CHECK_EQ_SNAPSHOT_OBJECT_ID(a1->GetId(), a2->GetId());
763 CHECK_EQ_SNAPSHOT_OBJECT_ID(k1->GetId(), k2->GetId());
764 }
765
766
TEST(HeapEntryIdsAndGC)767 TEST(HeapEntryIdsAndGC) {
768 LocalContext env;
769 v8::HandleScope scope(env->GetIsolate());
770 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
771
772 CompileRun(
773 "function A() {}\n"
774 "function B(x) { this.x = x; }\n"
775 "var a = new A();\n"
776 "var b = new B(a);");
777 v8::Local<v8::String> s1_str = v8_str("s1");
778 v8::Local<v8::String> s2_str = v8_str("s2");
779 const v8::HeapSnapshot* snapshot1 =
780 heap_profiler->TakeHeapSnapshot(s1_str);
781 CHECK(ValidateSnapshot(snapshot1));
782
783 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
784
785 const v8::HeapSnapshot* snapshot2 =
786 heap_profiler->TakeHeapSnapshot(s2_str);
787 CHECK(ValidateSnapshot(snapshot2));
788
789 CHECK_GT(snapshot1->GetMaxSnapshotJSObjectId(), 7000);
790 CHECK(snapshot1->GetMaxSnapshotJSObjectId() <=
791 snapshot2->GetMaxSnapshotJSObjectId());
792
793 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
794 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
795 CHECK_NE_SNAPSHOT_OBJECT_ID(0, global1->GetId());
796 CHECK_EQ_SNAPSHOT_OBJECT_ID(global1->GetId(), global2->GetId());
797 const v8::HeapGraphNode* A1 =
798 GetProperty(global1, v8::HeapGraphEdge::kProperty, "A");
799 CHECK_NE(NULL, A1);
800 const v8::HeapGraphNode* A2 =
801 GetProperty(global2, v8::HeapGraphEdge::kProperty, "A");
802 CHECK_NE(NULL, A2);
803 CHECK_NE_SNAPSHOT_OBJECT_ID(0, A1->GetId());
804 CHECK_EQ_SNAPSHOT_OBJECT_ID(A1->GetId(), A2->GetId());
805 const v8::HeapGraphNode* B1 =
806 GetProperty(global1, v8::HeapGraphEdge::kProperty, "B");
807 CHECK_NE(NULL, B1);
808 const v8::HeapGraphNode* B2 =
809 GetProperty(global2, v8::HeapGraphEdge::kProperty, "B");
810 CHECK_NE(NULL, B2);
811 CHECK_NE_SNAPSHOT_OBJECT_ID(0, B1->GetId());
812 CHECK_EQ_SNAPSHOT_OBJECT_ID(B1->GetId(), B2->GetId());
813 const v8::HeapGraphNode* a1 =
814 GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
815 CHECK_NE(NULL, a1);
816 const v8::HeapGraphNode* a2 =
817 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
818 CHECK_NE(NULL, a2);
819 CHECK_NE_SNAPSHOT_OBJECT_ID(0, a1->GetId());
820 CHECK_EQ_SNAPSHOT_OBJECT_ID(a1->GetId(), a2->GetId());
821 const v8::HeapGraphNode* b1 =
822 GetProperty(global1, v8::HeapGraphEdge::kProperty, "b");
823 CHECK_NE(NULL, b1);
824 const v8::HeapGraphNode* b2 =
825 GetProperty(global2, v8::HeapGraphEdge::kProperty, "b");
826 CHECK_NE(NULL, b2);
827 CHECK_NE_SNAPSHOT_OBJECT_ID(0, b1->GetId());
828 CHECK_EQ_SNAPSHOT_OBJECT_ID(b1->GetId(), b2->GetId());
829 }
830
831
TEST(HeapSnapshotRootPreservedAfterSorting)832 TEST(HeapSnapshotRootPreservedAfterSorting) {
833 LocalContext env;
834 v8::HandleScope scope(env->GetIsolate());
835 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
836 const v8::HeapSnapshot* snapshot =
837 heap_profiler->TakeHeapSnapshot(v8_str("s"));
838 CHECK(ValidateSnapshot(snapshot));
839 const v8::HeapGraphNode* root1 = snapshot->GetRoot();
840 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>(
841 snapshot))->GetSortedEntriesList();
842 const v8::HeapGraphNode* root2 = snapshot->GetRoot();
843 CHECK_EQ(root1, root2);
844 }
845
846
847 namespace {
848
849 class TestJSONStream : public v8::OutputStream {
850 public:
TestJSONStream()851 TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {}
TestJSONStream(int abort_countdown)852 explicit TestJSONStream(int abort_countdown)
853 : eos_signaled_(0), abort_countdown_(abort_countdown) {}
~TestJSONStream()854 virtual ~TestJSONStream() {}
EndOfStream()855 virtual void EndOfStream() { ++eos_signaled_; }
WriteAsciiChunk(char * buffer,int chars_written)856 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
857 if (abort_countdown_ > 0) --abort_countdown_;
858 if (abort_countdown_ == 0) return kAbort;
859 CHECK_GT(chars_written, 0);
860 i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
861 i::MemCopy(chunk.start(), buffer, chars_written);
862 return kContinue;
863 }
WriteUint32Chunk(uint32_t * buffer,int chars_written)864 virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int chars_written) {
865 DCHECK(false);
866 return kAbort;
867 }
WriteTo(i::Vector<char> dest)868 void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
eos_signaled()869 int eos_signaled() { return eos_signaled_; }
size()870 int size() { return buffer_.size(); }
871
872 private:
873 i::Collector<char> buffer_;
874 int eos_signaled_;
875 int abort_countdown_;
876 };
877
878 class OneByteResource : public v8::String::ExternalOneByteStringResource {
879 public:
OneByteResource(i::Vector<char> string)880 explicit OneByteResource(i::Vector<char> string) : data_(string.start()) {
881 length_ = string.length();
882 }
data() const883 virtual const char* data() const { return data_; }
length() const884 virtual size_t length() const { return length_; }
885 private:
886 const char* data_;
887 size_t length_;
888 };
889
890 } // namespace
891
TEST(HeapSnapshotJSONSerialization)892 TEST(HeapSnapshotJSONSerialization) {
893 LocalContext env;
894 v8::HandleScope scope(env->GetIsolate());
895 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
896
897 #define STRING_LITERAL_FOR_TEST \
898 "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
899 CompileRun(
900 "function A(s) { this.s = s; }\n"
901 "function B(x) { this.x = x; }\n"
902 "var a = new A(" STRING_LITERAL_FOR_TEST ");\n"
903 "var b = new B(a);");
904 const v8::HeapSnapshot* snapshot =
905 heap_profiler->TakeHeapSnapshot(v8_str("json"));
906 CHECK(ValidateSnapshot(snapshot));
907
908 TestJSONStream stream;
909 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
910 CHECK_GT(stream.size(), 0);
911 CHECK_EQ(1, stream.eos_signaled());
912 i::ScopedVector<char> json(stream.size());
913 stream.WriteTo(json);
914
915 // Verify that snapshot string is valid JSON.
916 OneByteResource* json_res = new OneByteResource(json);
917 v8::Local<v8::String> json_string =
918 v8::String::NewExternal(env->GetIsolate(), json_res);
919 env->Global()->Set(v8_str("json_snapshot"), json_string);
920 v8::Local<v8::Value> snapshot_parse_result = CompileRun(
921 "var parsed = JSON.parse(json_snapshot); true;");
922 CHECK(!snapshot_parse_result.IsEmpty());
923
924 // Verify that snapshot object has required fields.
925 v8::Local<v8::Object> parsed_snapshot =
926 env->Global()->Get(v8_str("parsed"))->ToObject();
927 CHECK(parsed_snapshot->Has(v8_str("snapshot")));
928 CHECK(parsed_snapshot->Has(v8_str("nodes")));
929 CHECK(parsed_snapshot->Has(v8_str("edges")));
930 CHECK(parsed_snapshot->Has(v8_str("strings")));
931
932 // Get node and edge "member" offsets.
933 v8::Local<v8::Value> meta_analysis_result = CompileRun(
934 "var meta = parsed.snapshot.meta;\n"
935 "var edge_count_offset = meta.node_fields.indexOf('edge_count');\n"
936 "var node_fields_count = meta.node_fields.length;\n"
937 "var edge_fields_count = meta.edge_fields.length;\n"
938 "var edge_type_offset = meta.edge_fields.indexOf('type');\n"
939 "var edge_name_offset = meta.edge_fields.indexOf('name_or_index');\n"
940 "var edge_to_node_offset = meta.edge_fields.indexOf('to_node');\n"
941 "var property_type ="
942 " meta.edge_types[edge_type_offset].indexOf('property');\n"
943 "var shortcut_type ="
944 " meta.edge_types[edge_type_offset].indexOf('shortcut');\n"
945 "var node_count = parsed.nodes.length / node_fields_count;\n"
946 "var first_edge_indexes = parsed.first_edge_indexes = [];\n"
947 "for (var i = 0, first_edge_index = 0; i < node_count; ++i) {\n"
948 " first_edge_indexes[i] = first_edge_index;\n"
949 " first_edge_index += edge_fields_count *\n"
950 " parsed.nodes[i * node_fields_count + edge_count_offset];\n"
951 "}\n"
952 "first_edge_indexes[node_count] = first_edge_index;\n");
953 CHECK(!meta_analysis_result.IsEmpty());
954
955 // A helper function for processing encoded nodes.
956 CompileRun(
957 "function GetChildPosByProperty(pos, prop_name, prop_type) {\n"
958 " var nodes = parsed.nodes;\n"
959 " var edges = parsed.edges;\n"
960 " var strings = parsed.strings;\n"
961 " var node_ordinal = pos / node_fields_count;\n"
962 " for (var i = parsed.first_edge_indexes[node_ordinal],\n"
963 " count = parsed.first_edge_indexes[node_ordinal + 1];\n"
964 " i < count; i += edge_fields_count) {\n"
965 " if (edges[i + edge_type_offset] === prop_type\n"
966 " && strings[edges[i + edge_name_offset]] === prop_name)\n"
967 " return edges[i + edge_to_node_offset];\n"
968 " }\n"
969 " return null;\n"
970 "}\n");
971 // Get the string index using the path: <root> -> <global>.b.x.s
972 v8::Local<v8::Value> string_obj_pos_val = CompileRun(
973 "GetChildPosByProperty(\n"
974 " GetChildPosByProperty(\n"
975 " GetChildPosByProperty("
976 " parsed.edges[edge_fields_count + edge_to_node_offset],"
977 " \"b\", property_type),\n"
978 " \"x\", property_type),"
979 " \"s\", property_type)");
980 CHECK(!string_obj_pos_val.IsEmpty());
981 int string_obj_pos =
982 static_cast<int>(string_obj_pos_val->ToNumber()->Value());
983 v8::Local<v8::Object> nodes_array =
984 parsed_snapshot->Get(v8_str("nodes"))->ToObject();
985 int string_index = static_cast<int>(
986 nodes_array->Get(string_obj_pos + 1)->ToNumber()->Value());
987 CHECK_GT(string_index, 0);
988 v8::Local<v8::Object> strings_array =
989 parsed_snapshot->Get(v8_str("strings"))->ToObject();
990 v8::Local<v8::String> string = strings_array->Get(string_index)->ToString();
991 v8::Local<v8::String> ref_string =
992 CompileRun(STRING_LITERAL_FOR_TEST)->ToString();
993 #undef STRING_LITERAL_FOR_TEST
994 CHECK_EQ(*v8::String::Utf8Value(ref_string),
995 *v8::String::Utf8Value(string));
996 }
997
998
TEST(HeapSnapshotJSONSerializationAborting)999 TEST(HeapSnapshotJSONSerializationAborting) {
1000 LocalContext env;
1001 v8::HandleScope scope(env->GetIsolate());
1002 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1003 const v8::HeapSnapshot* snapshot =
1004 heap_profiler->TakeHeapSnapshot(v8_str("abort"));
1005 CHECK(ValidateSnapshot(snapshot));
1006 TestJSONStream stream(5);
1007 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
1008 CHECK_GT(stream.size(), 0);
1009 CHECK_EQ(0, stream.eos_signaled());
1010 }
1011
1012 namespace {
1013
1014 class TestStatsStream : public v8::OutputStream {
1015 public:
TestStatsStream()1016 TestStatsStream()
1017 : eos_signaled_(0),
1018 updates_written_(0),
1019 entries_count_(0),
1020 entries_size_(0),
1021 intervals_count_(0),
1022 first_interval_index_(-1) { }
TestStatsStream(const TestStatsStream & stream)1023 TestStatsStream(const TestStatsStream& stream)
1024 : v8::OutputStream(stream),
1025 eos_signaled_(stream.eos_signaled_),
1026 updates_written_(stream.updates_written_),
1027 entries_count_(stream.entries_count_),
1028 entries_size_(stream.entries_size_),
1029 intervals_count_(stream.intervals_count_),
1030 first_interval_index_(stream.first_interval_index_) { }
~TestStatsStream()1031 virtual ~TestStatsStream() {}
EndOfStream()1032 virtual void EndOfStream() { ++eos_signaled_; }
WriteAsciiChunk(char * buffer,int chars_written)1033 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
1034 DCHECK(false);
1035 return kAbort;
1036 }
WriteHeapStatsChunk(v8::HeapStatsUpdate * buffer,int updates_written)1037 virtual WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* buffer,
1038 int updates_written) {
1039 ++intervals_count_;
1040 DCHECK(updates_written);
1041 updates_written_ += updates_written;
1042 entries_count_ = 0;
1043 if (first_interval_index_ == -1 && updates_written != 0)
1044 first_interval_index_ = buffer[0].index;
1045 for (int i = 0; i < updates_written; ++i) {
1046 entries_count_ += buffer[i].count;
1047 entries_size_ += buffer[i].size;
1048 }
1049
1050 return kContinue;
1051 }
eos_signaled()1052 int eos_signaled() { return eos_signaled_; }
updates_written()1053 int updates_written() { return updates_written_; }
entries_count() const1054 uint32_t entries_count() const { return entries_count_; }
entries_size() const1055 uint32_t entries_size() const { return entries_size_; }
intervals_count() const1056 int intervals_count() const { return intervals_count_; }
first_interval_index() const1057 int first_interval_index() const { return first_interval_index_; }
1058
1059 private:
1060 int eos_signaled_;
1061 int updates_written_;
1062 uint32_t entries_count_;
1063 uint32_t entries_size_;
1064 int intervals_count_;
1065 int first_interval_index_;
1066 };
1067
1068 } // namespace
1069
GetHeapStatsUpdate(v8::HeapProfiler * heap_profiler,v8::SnapshotObjectId * object_id=NULL)1070 static TestStatsStream GetHeapStatsUpdate(
1071 v8::HeapProfiler* heap_profiler,
1072 v8::SnapshotObjectId* object_id = NULL) {
1073 TestStatsStream stream;
1074 v8::SnapshotObjectId last_seen_id = heap_profiler->GetHeapStats(&stream);
1075 if (object_id)
1076 *object_id = last_seen_id;
1077 CHECK_EQ(1, stream.eos_signaled());
1078 return stream;
1079 }
1080
1081
TEST(HeapSnapshotObjectsStats)1082 TEST(HeapSnapshotObjectsStats) {
1083 LocalContext env;
1084 v8::HandleScope scope(env->GetIsolate());
1085 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1086
1087 heap_profiler->StartTrackingHeapObjects();
1088 // We have to call GC 6 times. In other case the garbage will be
1089 // the reason of flakiness.
1090 for (int i = 0; i < 6; ++i) {
1091 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
1092 }
1093
1094 v8::SnapshotObjectId initial_id;
1095 {
1096 // Single chunk of data expected in update. Initial data.
1097 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler,
1098 &initial_id);
1099 CHECK_EQ(1, stats_update.intervals_count());
1100 CHECK_EQ(1, stats_update.updates_written());
1101 CHECK_LT(0, stats_update.entries_size());
1102 CHECK_EQ(0, stats_update.first_interval_index());
1103 }
1104
1105 // No data expected in update because nothing has happened.
1106 v8::SnapshotObjectId same_id;
1107 CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &same_id).updates_written());
1108 CHECK_EQ_SNAPSHOT_OBJECT_ID(initial_id, same_id);
1109
1110 {
1111 v8::SnapshotObjectId additional_string_id;
1112 v8::HandleScope inner_scope_1(env->GetIsolate());
1113 v8_str("string1");
1114 {
1115 // Single chunk of data with one new entry expected in update.
1116 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler,
1117 &additional_string_id);
1118 CHECK_LT(same_id, additional_string_id);
1119 CHECK_EQ(1, stats_update.intervals_count());
1120 CHECK_EQ(1, stats_update.updates_written());
1121 CHECK_LT(0, stats_update.entries_size());
1122 CHECK_EQ(1, stats_update.entries_count());
1123 CHECK_EQ(2, stats_update.first_interval_index());
1124 }
1125
1126 // No data expected in update because nothing happened.
1127 v8::SnapshotObjectId last_id;
1128 CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &last_id).updates_written());
1129 CHECK_EQ_SNAPSHOT_OBJECT_ID(additional_string_id, last_id);
1130
1131 {
1132 v8::HandleScope inner_scope_2(env->GetIsolate());
1133 v8_str("string2");
1134
1135 uint32_t entries_size;
1136 {
1137 v8::HandleScope inner_scope_3(env->GetIsolate());
1138 v8_str("string3");
1139 v8_str("string4");
1140
1141 {
1142 // Single chunk of data with three new 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_LT(0, entries_size = stats_update.entries_size());
1147 CHECK_EQ(3, stats_update.entries_count());
1148 CHECK_EQ(4, stats_update.first_interval_index());
1149 }
1150 }
1151
1152 {
1153 // Single chunk of data with two left entries expected in update.
1154 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1155 CHECK_EQ(1, stats_update.intervals_count());
1156 CHECK_EQ(1, stats_update.updates_written());
1157 CHECK_GT(entries_size, stats_update.entries_size());
1158 CHECK_EQ(1, stats_update.entries_count());
1159 // Two strings from forth interval were released.
1160 CHECK_EQ(4, stats_update.first_interval_index());
1161 }
1162 }
1163
1164 {
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(0, stats_update.entries_size());
1170 CHECK_EQ(0, stats_update.entries_count());
1171 // The last string from forth interval was released.
1172 CHECK_EQ(4, stats_update.first_interval_index());
1173 }
1174 }
1175 {
1176 // Single chunk of data with 0 left entries expected in update.
1177 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1178 CHECK_EQ(1, stats_update.intervals_count());
1179 CHECK_EQ(1, stats_update.updates_written());
1180 CHECK_EQ(0, stats_update.entries_size());
1181 CHECK_EQ(0, stats_update.entries_count());
1182 // The only string from the second interval was released.
1183 CHECK_EQ(2, stats_update.first_interval_index());
1184 }
1185
1186 v8::Local<v8::Array> array = v8::Array::New(env->GetIsolate());
1187 CHECK_EQ(0, array->Length());
1188 // Force array's buffer allocation.
1189 array->Set(2, v8_num(7));
1190
1191 uint32_t entries_size;
1192 {
1193 // Single chunk of data with 2 entries expected in update.
1194 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1195 CHECK_EQ(1, stats_update.intervals_count());
1196 CHECK_EQ(1, stats_update.updates_written());
1197 CHECK_LT(0, entries_size = stats_update.entries_size());
1198 // They are the array and its buffer.
1199 CHECK_EQ(2, stats_update.entries_count());
1200 CHECK_EQ(8, stats_update.first_interval_index());
1201 }
1202
1203 for (int i = 0; i < 100; ++i)
1204 array->Set(i, v8_num(i));
1205
1206 {
1207 // Single chunk of data with 1 entry expected in update.
1208 TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
1209 CHECK_EQ(1, stats_update.intervals_count());
1210 // The first interval was changed because old buffer was collected.
1211 // The second interval was changed because new buffer was allocated.
1212 CHECK_EQ(2, stats_update.updates_written());
1213 CHECK_LT(entries_size, stats_update.entries_size());
1214 CHECK_EQ(2, stats_update.entries_count());
1215 CHECK_EQ(8, stats_update.first_interval_index());
1216 }
1217
1218 heap_profiler->StopTrackingHeapObjects();
1219 }
1220
1221
TEST(HeapObjectIds)1222 TEST(HeapObjectIds) {
1223 LocalContext env;
1224 v8::Isolate* isolate = env->GetIsolate();
1225 v8::HandleScope scope(isolate);
1226 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1227
1228 const int kLength = 10;
1229 v8::Handle<v8::Object> objects[kLength];
1230 v8::SnapshotObjectId ids[kLength];
1231
1232 heap_profiler->StartTrackingHeapObjects(false);
1233
1234 for (int i = 0; i < kLength; i++) {
1235 objects[i] = v8::Object::New(isolate);
1236 }
1237 GetHeapStatsUpdate(heap_profiler);
1238
1239 for (int i = 0; i < kLength; i++) {
1240 v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
1241 CHECK_NE(v8::HeapProfiler::kUnknownObjectId, static_cast<int>(id));
1242 ids[i] = id;
1243 }
1244
1245 heap_profiler->StopTrackingHeapObjects();
1246 CcTest::heap()->CollectAllAvailableGarbage();
1247
1248 for (int i = 0; i < kLength; i++) {
1249 v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
1250 CHECK_EQ(static_cast<int>(ids[i]), static_cast<int>(id));
1251 v8::Handle<v8::Value> obj = heap_profiler->FindObjectById(ids[i]);
1252 CHECK_EQ(objects[i], obj);
1253 }
1254
1255 heap_profiler->ClearObjectIds();
1256 for (int i = 0; i < kLength; i++) {
1257 v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
1258 CHECK_EQ(v8::HeapProfiler::kUnknownObjectId, static_cast<int>(id));
1259 v8::Handle<v8::Value> obj = heap_profiler->FindObjectById(ids[i]);
1260 CHECK(obj.IsEmpty());
1261 }
1262 }
1263
1264
CheckChildrenIds(const v8::HeapSnapshot * snapshot,const v8::HeapGraphNode * node,int level,int max_level)1265 static void CheckChildrenIds(const v8::HeapSnapshot* snapshot,
1266 const v8::HeapGraphNode* node,
1267 int level, int max_level) {
1268 if (level > max_level) return;
1269 CHECK_EQ(node, snapshot->GetNodeById(node->GetId()));
1270 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
1271 const v8::HeapGraphEdge* prop = node->GetChild(i);
1272 const v8::HeapGraphNode* child =
1273 snapshot->GetNodeById(prop->GetToNode()->GetId());
1274 CHECK_EQ_SNAPSHOT_OBJECT_ID(prop->GetToNode()->GetId(), child->GetId());
1275 CHECK_EQ(prop->GetToNode(), child);
1276 CheckChildrenIds(snapshot, child, level + 1, max_level);
1277 }
1278 }
1279
1280
TEST(HeapSnapshotGetNodeById)1281 TEST(HeapSnapshotGetNodeById) {
1282 LocalContext env;
1283 v8::HandleScope scope(env->GetIsolate());
1284 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1285
1286 const v8::HeapSnapshot* snapshot =
1287 heap_profiler->TakeHeapSnapshot(v8_str("id"));
1288 CHECK(ValidateSnapshot(snapshot));
1289 const v8::HeapGraphNode* root = snapshot->GetRoot();
1290 CheckChildrenIds(snapshot, root, 0, 3);
1291 // Check a big id, which should not exist yet.
1292 CHECK_EQ(NULL, snapshot->GetNodeById(0x1000000UL));
1293 }
1294
1295
TEST(HeapSnapshotGetSnapshotObjectId)1296 TEST(HeapSnapshotGetSnapshotObjectId) {
1297 LocalContext env;
1298 v8::HandleScope scope(env->GetIsolate());
1299 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1300 CompileRun("globalObject = {};\n");
1301 const v8::HeapSnapshot* snapshot =
1302 heap_profiler->TakeHeapSnapshot(v8_str("get_snapshot_object_id"));
1303 CHECK(ValidateSnapshot(snapshot));
1304 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1305 const v8::HeapGraphNode* global_object =
1306 GetProperty(global, v8::HeapGraphEdge::kProperty, "globalObject");
1307 CHECK(global_object);
1308
1309 v8::Local<v8::Value> globalObjectHandle = env->Global()->Get(
1310 v8::String::NewFromUtf8(env->GetIsolate(), "globalObject"));
1311 CHECK(!globalObjectHandle.IsEmpty());
1312 CHECK(globalObjectHandle->IsObject());
1313
1314 v8::SnapshotObjectId id = heap_profiler->GetObjectId(globalObjectHandle);
1315 CHECK_NE(static_cast<int>(v8::HeapProfiler::kUnknownObjectId),
1316 id);
1317 CHECK_EQ(static_cast<int>(id), global_object->GetId());
1318 }
1319
1320
TEST(HeapSnapshotUnknownSnapshotObjectId)1321 TEST(HeapSnapshotUnknownSnapshotObjectId) {
1322 LocalContext env;
1323 v8::HandleScope scope(env->GetIsolate());
1324 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1325 CompileRun("globalObject = {};\n");
1326 const v8::HeapSnapshot* snapshot =
1327 heap_profiler->TakeHeapSnapshot(v8_str("unknown_object_id"));
1328 CHECK(ValidateSnapshot(snapshot));
1329 const v8::HeapGraphNode* node =
1330 snapshot->GetNodeById(v8::HeapProfiler::kUnknownObjectId);
1331 CHECK_EQ(NULL, node);
1332 }
1333
1334
1335 namespace {
1336
1337 class TestActivityControl : public v8::ActivityControl {
1338 public:
TestActivityControl(int abort_count)1339 explicit TestActivityControl(int abort_count)
1340 : done_(0), total_(0), abort_count_(abort_count) {}
ReportProgressValue(int done,int total)1341 ControlOption ReportProgressValue(int done, int total) {
1342 done_ = done;
1343 total_ = total;
1344 return --abort_count_ != 0 ? kContinue : kAbort;
1345 }
done()1346 int done() { return done_; }
total()1347 int total() { return total_; }
1348
1349 private:
1350 int done_;
1351 int total_;
1352 int abort_count_;
1353 };
1354 }
1355
1356
TEST(TakeHeapSnapshotAborting)1357 TEST(TakeHeapSnapshotAborting) {
1358 LocalContext env;
1359 v8::HandleScope scope(env->GetIsolate());
1360
1361 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1362 const int snapshots_count = heap_profiler->GetSnapshotCount();
1363 TestActivityControl aborting_control(1);
1364 const v8::HeapSnapshot* no_snapshot =
1365 heap_profiler->TakeHeapSnapshot(v8_str("abort"),
1366 &aborting_control);
1367 CHECK_EQ(NULL, no_snapshot);
1368 CHECK_EQ(snapshots_count, heap_profiler->GetSnapshotCount());
1369 CHECK_GT(aborting_control.total(), aborting_control.done());
1370
1371 TestActivityControl control(-1); // Don't abort.
1372 const v8::HeapSnapshot* snapshot =
1373 heap_profiler->TakeHeapSnapshot(v8_str("full"),
1374 &control);
1375 CHECK(ValidateSnapshot(snapshot));
1376
1377 CHECK_NE(NULL, snapshot);
1378 CHECK_EQ(snapshots_count + 1, heap_profiler->GetSnapshotCount());
1379 CHECK_EQ(control.total(), control.done());
1380 CHECK_GT(control.total(), 0);
1381 }
1382
1383
1384 namespace {
1385
1386 class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
1387 public:
TestRetainedObjectInfo(int hash,const char * group_label,const char * label,intptr_t element_count=-1,intptr_t size=-1)1388 TestRetainedObjectInfo(int hash,
1389 const char* group_label,
1390 const char* label,
1391 intptr_t element_count = -1,
1392 intptr_t size = -1)
1393 : disposed_(false),
1394 hash_(hash),
1395 group_label_(group_label),
1396 label_(label),
1397 element_count_(element_count),
1398 size_(size) {
1399 instances.Add(this);
1400 }
~TestRetainedObjectInfo()1401 virtual ~TestRetainedObjectInfo() {}
Dispose()1402 virtual void Dispose() {
1403 CHECK(!disposed_);
1404 disposed_ = true;
1405 }
IsEquivalent(RetainedObjectInfo * other)1406 virtual bool IsEquivalent(RetainedObjectInfo* other) {
1407 return GetHash() == other->GetHash();
1408 }
GetHash()1409 virtual intptr_t GetHash() { return hash_; }
GetGroupLabel()1410 virtual const char* GetGroupLabel() { return group_label_; }
GetLabel()1411 virtual const char* GetLabel() { return label_; }
GetElementCount()1412 virtual intptr_t GetElementCount() { return element_count_; }
GetSizeInBytes()1413 virtual intptr_t GetSizeInBytes() { return size_; }
disposed()1414 bool disposed() { return disposed_; }
1415
WrapperInfoCallback(uint16_t class_id,v8::Handle<v8::Value> wrapper)1416 static v8::RetainedObjectInfo* WrapperInfoCallback(
1417 uint16_t class_id, v8::Handle<v8::Value> wrapper) {
1418 if (class_id == 1) {
1419 if (wrapper->IsString()) {
1420 v8::String::Utf8Value utf8(wrapper);
1421 if (strcmp(*utf8, "AAA") == 0)
1422 return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
1423 else if (strcmp(*utf8, "BBB") == 0)
1424 return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
1425 }
1426 } else if (class_id == 2) {
1427 if (wrapper->IsString()) {
1428 v8::String::Utf8Value utf8(wrapper);
1429 if (strcmp(*utf8, "CCC") == 0)
1430 return new TestRetainedObjectInfo(2, "ccc-group", "ccc");
1431 }
1432 }
1433 CHECK(false);
1434 return NULL;
1435 }
1436
1437 static i::List<TestRetainedObjectInfo*> instances;
1438
1439 private:
1440 bool disposed_;
1441 int hash_;
1442 const char* group_label_;
1443 const char* label_;
1444 intptr_t element_count_;
1445 intptr_t size_;
1446 };
1447
1448
1449 i::List<TestRetainedObjectInfo*> TestRetainedObjectInfo::instances;
1450 }
1451
1452
GetNode(const v8::HeapGraphNode * parent,v8::HeapGraphNode::Type type,const char * name)1453 static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent,
1454 v8::HeapGraphNode::Type type,
1455 const char* name) {
1456 for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) {
1457 const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode();
1458 if (node->GetType() == type && strcmp(name,
1459 const_cast<i::HeapEntry*>(
1460 reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) {
1461 return node;
1462 }
1463 }
1464 return NULL;
1465 }
1466
1467
TEST(HeapSnapshotRetainedObjectInfo)1468 TEST(HeapSnapshotRetainedObjectInfo) {
1469 LocalContext env;
1470 v8::Isolate* isolate = env->GetIsolate();
1471 v8::HandleScope scope(isolate);
1472 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
1473
1474 heap_profiler->SetWrapperClassInfoProvider(
1475 1, TestRetainedObjectInfo::WrapperInfoCallback);
1476 heap_profiler->SetWrapperClassInfoProvider(
1477 2, TestRetainedObjectInfo::WrapperInfoCallback);
1478 v8::Persistent<v8::String> p_AAA(isolate, v8_str("AAA"));
1479 p_AAA.SetWrapperClassId(1);
1480 v8::Persistent<v8::String> p_BBB(isolate, v8_str("BBB"));
1481 p_BBB.SetWrapperClassId(1);
1482 v8::Persistent<v8::String> p_CCC(isolate, v8_str("CCC"));
1483 p_CCC.SetWrapperClassId(2);
1484 CHECK_EQ(0, TestRetainedObjectInfo::instances.length());
1485 const v8::HeapSnapshot* snapshot =
1486 heap_profiler->TakeHeapSnapshot(v8_str("retained"));
1487 CHECK(ValidateSnapshot(snapshot));
1488
1489 CHECK_EQ(3, TestRetainedObjectInfo::instances.length());
1490 for (int i = 0; i < TestRetainedObjectInfo::instances.length(); ++i) {
1491 CHECK(TestRetainedObjectInfo::instances[i]->disposed());
1492 delete TestRetainedObjectInfo::instances[i];
1493 }
1494
1495 const v8::HeapGraphNode* native_group_aaa = GetNode(
1496 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "aaa-group");
1497 CHECK_NE(NULL, native_group_aaa);
1498 CHECK_EQ(1, native_group_aaa->GetChildrenCount());
1499 const v8::HeapGraphNode* aaa = GetNode(
1500 native_group_aaa, v8::HeapGraphNode::kNative, "aaa / 100 entries");
1501 CHECK_NE(NULL, aaa);
1502 CHECK_EQ(2, aaa->GetChildrenCount());
1503
1504 const v8::HeapGraphNode* native_group_ccc = GetNode(
1505 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "ccc-group");
1506 const v8::HeapGraphNode* ccc = GetNode(
1507 native_group_ccc, v8::HeapGraphNode::kNative, "ccc");
1508 CHECK_NE(NULL, ccc);
1509
1510 const v8::HeapGraphNode* n_AAA = GetNode(
1511 aaa, v8::HeapGraphNode::kString, "AAA");
1512 CHECK_NE(NULL, n_AAA);
1513 const v8::HeapGraphNode* n_BBB = GetNode(
1514 aaa, v8::HeapGraphNode::kString, "BBB");
1515 CHECK_NE(NULL, n_BBB);
1516 CHECK_EQ(1, ccc->GetChildrenCount());
1517 const v8::HeapGraphNode* n_CCC = GetNode(
1518 ccc, v8::HeapGraphNode::kString, "CCC");
1519 CHECK_NE(NULL, n_CCC);
1520
1521 CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "native"));
1522 CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "native"));
1523 CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "native"));
1524 }
1525
1526
1527 class GraphWithImplicitRefs {
1528 public:
1529 static const int kObjectsCount = 4;
GraphWithImplicitRefs(LocalContext * env)1530 explicit GraphWithImplicitRefs(LocalContext* env) {
1531 CHECK_EQ(NULL, instance_);
1532 instance_ = this;
1533 isolate_ = (*env)->GetIsolate();
1534 for (int i = 0; i < kObjectsCount; i++) {
1535 objects_[i].Reset(isolate_, v8::Object::New(isolate_));
1536 }
1537 (*env)->Global()->Set(v8_str("root_object"),
1538 v8::Local<v8::Value>::New(isolate_, objects_[0]));
1539 }
~GraphWithImplicitRefs()1540 ~GraphWithImplicitRefs() {
1541 instance_ = NULL;
1542 }
1543
gcPrologue(v8::GCType type,v8::GCCallbackFlags flags)1544 static void gcPrologue(v8::GCType type, v8::GCCallbackFlags flags) {
1545 instance_->AddImplicitReferences();
1546 }
1547
1548 private:
AddImplicitReferences()1549 void AddImplicitReferences() {
1550 // 0 -> 1
1551 isolate_->SetObjectGroupId(objects_[0],
1552 v8::UniqueId(1));
1553 isolate_->SetReferenceFromGroup(
1554 v8::UniqueId(1), objects_[1]);
1555 // Adding two more references: 1 -> 2, 1 -> 3
1556 isolate_->SetReference(objects_[1].As<v8::Object>(),
1557 objects_[2]);
1558 isolate_->SetReference(objects_[1].As<v8::Object>(),
1559 objects_[3]);
1560 }
1561
1562 v8::Persistent<v8::Value> objects_[kObjectsCount];
1563 static GraphWithImplicitRefs* instance_;
1564 v8::Isolate* isolate_;
1565 };
1566
1567 GraphWithImplicitRefs* GraphWithImplicitRefs::instance_ = NULL;
1568
1569
TEST(HeapSnapshotImplicitReferences)1570 TEST(HeapSnapshotImplicitReferences) {
1571 LocalContext env;
1572 v8::HandleScope scope(env->GetIsolate());
1573 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1574
1575 GraphWithImplicitRefs graph(&env);
1576 v8::V8::AddGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue);
1577
1578 const v8::HeapSnapshot* snapshot =
1579 heap_profiler->TakeHeapSnapshot(v8_str("implicit_refs"));
1580 CHECK(ValidateSnapshot(snapshot));
1581
1582 const v8::HeapGraphNode* global_object = GetGlobalObject(snapshot);
1583 const v8::HeapGraphNode* obj0 = GetProperty(
1584 global_object, v8::HeapGraphEdge::kProperty, "root_object");
1585 CHECK(obj0);
1586 CHECK_EQ(v8::HeapGraphNode::kObject, obj0->GetType());
1587 const v8::HeapGraphNode* obj1 = GetProperty(
1588 obj0, v8::HeapGraphEdge::kInternal, "native");
1589 CHECK(obj1);
1590 int implicit_targets_count = 0;
1591 for (int i = 0, count = obj1->GetChildrenCount(); i < count; ++i) {
1592 const v8::HeapGraphEdge* prop = obj1->GetChild(i);
1593 v8::String::Utf8Value prop_name(prop->GetName());
1594 if (prop->GetType() == v8::HeapGraphEdge::kInternal &&
1595 strcmp("native", *prop_name) == 0) {
1596 ++implicit_targets_count;
1597 }
1598 }
1599 CHECK_EQ(2, implicit_targets_count);
1600 v8::V8::RemoveGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue);
1601 }
1602
1603
TEST(DeleteAllHeapSnapshots)1604 TEST(DeleteAllHeapSnapshots) {
1605 LocalContext env;
1606 v8::HandleScope scope(env->GetIsolate());
1607 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1608
1609 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1610 heap_profiler->DeleteAllHeapSnapshots();
1611 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1612 CHECK_NE(NULL, heap_profiler->TakeHeapSnapshot(v8_str("1")));
1613 CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1614 heap_profiler->DeleteAllHeapSnapshots();
1615 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1616 CHECK_NE(NULL, heap_profiler->TakeHeapSnapshot(v8_str("1")));
1617 CHECK_NE(NULL, heap_profiler->TakeHeapSnapshot(v8_str("2")));
1618 CHECK_EQ(2, heap_profiler->GetSnapshotCount());
1619 heap_profiler->DeleteAllHeapSnapshots();
1620 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1621 }
1622
1623
FindHeapSnapshot(v8::HeapProfiler * profiler,unsigned uid)1624 static const v8::HeapSnapshot* FindHeapSnapshot(v8::HeapProfiler* profiler,
1625 unsigned uid) {
1626 int length = profiler->GetSnapshotCount();
1627 for (int i = 0; i < length; i++) {
1628 const v8::HeapSnapshot* snapshot = profiler->GetHeapSnapshot(i);
1629 if (snapshot->GetUid() == uid) {
1630 return snapshot;
1631 }
1632 }
1633 return NULL;
1634 }
1635
1636
TEST(DeleteHeapSnapshot)1637 TEST(DeleteHeapSnapshot) {
1638 LocalContext env;
1639 v8::HandleScope scope(env->GetIsolate());
1640 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1641
1642 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1643 const v8::HeapSnapshot* s1 =
1644 heap_profiler->TakeHeapSnapshot(v8_str("1"));
1645
1646 CHECK_NE(NULL, s1);
1647 CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1648 unsigned uid1 = s1->GetUid();
1649 CHECK_EQ(s1, FindHeapSnapshot(heap_profiler, uid1));
1650 const_cast<v8::HeapSnapshot*>(s1)->Delete();
1651 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1652 CHECK_EQ(NULL, FindHeapSnapshot(heap_profiler, uid1));
1653
1654 const v8::HeapSnapshot* s2 =
1655 heap_profiler->TakeHeapSnapshot(v8_str("2"));
1656 CHECK_NE(NULL, s2);
1657 CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1658 unsigned uid2 = s2->GetUid();
1659 CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid2));
1660 CHECK_EQ(s2, FindHeapSnapshot(heap_profiler, uid2));
1661 const v8::HeapSnapshot* s3 =
1662 heap_profiler->TakeHeapSnapshot(v8_str("3"));
1663 CHECK_NE(NULL, s3);
1664 CHECK_EQ(2, heap_profiler->GetSnapshotCount());
1665 unsigned uid3 = s3->GetUid();
1666 CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid3));
1667 CHECK_EQ(s3, FindHeapSnapshot(heap_profiler, uid3));
1668 const_cast<v8::HeapSnapshot*>(s2)->Delete();
1669 CHECK_EQ(1, heap_profiler->GetSnapshotCount());
1670 CHECK_EQ(NULL, FindHeapSnapshot(heap_profiler, uid2));
1671 CHECK_EQ(s3, FindHeapSnapshot(heap_profiler, uid3));
1672 const_cast<v8::HeapSnapshot*>(s3)->Delete();
1673 CHECK_EQ(0, heap_profiler->GetSnapshotCount());
1674 CHECK_EQ(NULL, FindHeapSnapshot(heap_profiler, uid3));
1675 }
1676
1677
1678 class NameResolver : public v8::HeapProfiler::ObjectNameResolver {
1679 public:
GetName(v8::Handle<v8::Object> object)1680 virtual const char* GetName(v8::Handle<v8::Object> object) {
1681 return "Global object name";
1682 }
1683 };
1684
1685
TEST(GlobalObjectName)1686 TEST(GlobalObjectName) {
1687 LocalContext env;
1688 v8::HandleScope scope(env->GetIsolate());
1689 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1690
1691 CompileRun("document = { URL:\"abcdefgh\" };");
1692
1693 NameResolver name_resolver;
1694 const v8::HeapSnapshot* snapshot =
1695 heap_profiler->TakeHeapSnapshot(v8_str("document"),
1696 NULL,
1697 &name_resolver);
1698 CHECK(ValidateSnapshot(snapshot));
1699 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1700 CHECK_NE(NULL, global);
1701 CHECK_EQ("Object / Global object name" ,
1702 const_cast<i::HeapEntry*>(
1703 reinterpret_cast<const i::HeapEntry*>(global))->name());
1704 }
1705
1706
TEST(GlobalObjectFields)1707 TEST(GlobalObjectFields) {
1708 LocalContext env;
1709 v8::HandleScope scope(env->GetIsolate());
1710 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1711 CompileRun("obj = {};");
1712 const v8::HeapSnapshot* snapshot =
1713 heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
1714 CHECK(ValidateSnapshot(snapshot));
1715 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1716 const v8::HeapGraphNode* builtins =
1717 GetProperty(global, v8::HeapGraphEdge::kInternal, "builtins");
1718 CHECK_NE(NULL, builtins);
1719 const v8::HeapGraphNode* native_context =
1720 GetProperty(global, v8::HeapGraphEdge::kInternal, "native_context");
1721 CHECK_NE(NULL, native_context);
1722 const v8::HeapGraphNode* global_context =
1723 GetProperty(global, v8::HeapGraphEdge::kInternal, "global_context");
1724 CHECK_NE(NULL, global_context);
1725 const v8::HeapGraphNode* global_proxy =
1726 GetProperty(global, v8::HeapGraphEdge::kInternal, "global_proxy");
1727 CHECK_NE(NULL, global_proxy);
1728 }
1729
1730
TEST(NoHandleLeaks)1731 TEST(NoHandleLeaks) {
1732 LocalContext env;
1733 v8::HandleScope scope(env->GetIsolate());
1734 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1735
1736 CompileRun("document = { URL:\"abcdefgh\" };");
1737
1738 v8::Handle<v8::String> name(v8_str("leakz"));
1739 i::Isolate* isolate = CcTest::i_isolate();
1740 int count_before = i::HandleScope::NumberOfHandles(isolate);
1741 heap_profiler->TakeHeapSnapshot(name);
1742 int count_after = i::HandleScope::NumberOfHandles(isolate);
1743 CHECK_EQ(count_before, count_after);
1744 }
1745
1746
TEST(NodesIteration)1747 TEST(NodesIteration) {
1748 LocalContext env;
1749 v8::HandleScope scope(env->GetIsolate());
1750 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1751 const v8::HeapSnapshot* snapshot =
1752 heap_profiler->TakeHeapSnapshot(v8_str("iteration"));
1753 CHECK(ValidateSnapshot(snapshot));
1754 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1755 CHECK_NE(NULL, global);
1756 // Verify that we can find this object by iteration.
1757 const int nodes_count = snapshot->GetNodesCount();
1758 int count = 0;
1759 for (int i = 0; i < nodes_count; ++i) {
1760 if (snapshot->GetNode(i) == global)
1761 ++count;
1762 }
1763 CHECK_EQ(1, count);
1764 }
1765
1766
TEST(GetHeapValueForNode)1767 TEST(GetHeapValueForNode) {
1768 LocalContext env;
1769 v8::HandleScope scope(env->GetIsolate());
1770 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1771
1772 CompileRun("a = { s_prop: \'value\', n_prop: \'value2\' };");
1773 const v8::HeapSnapshot* snapshot =
1774 heap_profiler->TakeHeapSnapshot(v8_str("value"));
1775 CHECK(ValidateSnapshot(snapshot));
1776 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1777 CHECK(heap_profiler->FindObjectById(global->GetId())->IsObject());
1778 v8::Local<v8::Object> js_global =
1779 env->Global()->GetPrototype().As<v8::Object>();
1780 CHECK(js_global == heap_profiler->FindObjectById(global->GetId()));
1781 const v8::HeapGraphNode* obj = GetProperty(
1782 global, v8::HeapGraphEdge::kProperty, "a");
1783 CHECK(heap_profiler->FindObjectById(obj->GetId())->IsObject());
1784 v8::Local<v8::Object> js_obj = js_global->Get(v8_str("a")).As<v8::Object>();
1785 CHECK(js_obj == heap_profiler->FindObjectById(obj->GetId()));
1786 const v8::HeapGraphNode* s_prop =
1787 GetProperty(obj, v8::HeapGraphEdge::kProperty, "s_prop");
1788 v8::Local<v8::String> js_s_prop =
1789 js_obj->Get(v8_str("s_prop")).As<v8::String>();
1790 CHECK(js_s_prop == heap_profiler->FindObjectById(s_prop->GetId()));
1791 const v8::HeapGraphNode* n_prop =
1792 GetProperty(obj, v8::HeapGraphEdge::kProperty, "n_prop");
1793 v8::Local<v8::String> js_n_prop =
1794 js_obj->Get(v8_str("n_prop")).As<v8::String>();
1795 CHECK(js_n_prop == heap_profiler->FindObjectById(n_prop->GetId()));
1796 }
1797
1798
TEST(GetHeapValueForDeletedObject)1799 TEST(GetHeapValueForDeletedObject) {
1800 LocalContext env;
1801 v8::HandleScope scope(env->GetIsolate());
1802 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1803
1804 // It is impossible to delete a global property, so we are about to delete a
1805 // property of the "a" object. Also, the "p" object can't be an empty one
1806 // because the empty object is static and isn't actually deleted.
1807 CompileRun("a = { p: { r: {} } };");
1808 const v8::HeapSnapshot* snapshot =
1809 heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
1810 CHECK(ValidateSnapshot(snapshot));
1811 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1812 const v8::HeapGraphNode* obj = GetProperty(
1813 global, v8::HeapGraphEdge::kProperty, "a");
1814 const v8::HeapGraphNode* prop = GetProperty(
1815 obj, v8::HeapGraphEdge::kProperty, "p");
1816 {
1817 // Perform the check inside a nested local scope to avoid creating a
1818 // reference to the object we are deleting.
1819 v8::HandleScope scope(env->GetIsolate());
1820 CHECK(heap_profiler->FindObjectById(prop->GetId())->IsObject());
1821 }
1822 CompileRun("delete a.p;");
1823 CHECK(heap_profiler->FindObjectById(prop->GetId()).IsEmpty());
1824 }
1825
1826
StringCmp(const char * ref,i::String * act)1827 static int StringCmp(const char* ref, i::String* act) {
1828 i::SmartArrayPointer<char> s_act = act->ToCString();
1829 int result = strcmp(ref, s_act.get());
1830 if (result != 0)
1831 fprintf(stderr, "Expected: \"%s\", Actual: \"%s\"\n", ref, s_act.get());
1832 return result;
1833 }
1834
1835
TEST(GetConstructorName)1836 TEST(GetConstructorName) {
1837 LocalContext env;
1838 v8::HandleScope scope(env->GetIsolate());
1839
1840 CompileRun(
1841 "function Constructor1() {};\n"
1842 "var obj1 = new Constructor1();\n"
1843 "var Constructor2 = function() {};\n"
1844 "var obj2 = new Constructor2();\n"
1845 "var obj3 = {};\n"
1846 "obj3.constructor = function Constructor3() {};\n"
1847 "var obj4 = {};\n"
1848 "// Slow properties\n"
1849 "for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n"
1850 "obj4.constructor = function Constructor4() {};\n"
1851 "var obj5 = {};\n"
1852 "var obj6 = {};\n"
1853 "obj6.constructor = 6;");
1854 v8::Local<v8::Object> js_global =
1855 env->Global()->GetPrototype().As<v8::Object>();
1856 v8::Local<v8::Object> obj1 = js_global->Get(v8_str("obj1")).As<v8::Object>();
1857 i::Handle<i::JSObject> js_obj1 = v8::Utils::OpenHandle(*obj1);
1858 CHECK_EQ(0, StringCmp(
1859 "Constructor1", i::V8HeapExplorer::GetConstructorName(*js_obj1)));
1860 v8::Local<v8::Object> obj2 = js_global->Get(v8_str("obj2")).As<v8::Object>();
1861 i::Handle<i::JSObject> js_obj2 = v8::Utils::OpenHandle(*obj2);
1862 CHECK_EQ(0, StringCmp(
1863 "Constructor2", i::V8HeapExplorer::GetConstructorName(*js_obj2)));
1864 v8::Local<v8::Object> obj3 = js_global->Get(v8_str("obj3")).As<v8::Object>();
1865 i::Handle<i::JSObject> js_obj3 = v8::Utils::OpenHandle(*obj3);
1866 // TODO(verwaest): Restore to Constructor3 once supported by the
1867 // heap-snapshot-generator.
1868 CHECK_EQ(
1869 0, StringCmp("Object", i::V8HeapExplorer::GetConstructorName(*js_obj3)));
1870 v8::Local<v8::Object> obj4 = js_global->Get(v8_str("obj4")).As<v8::Object>();
1871 i::Handle<i::JSObject> js_obj4 = v8::Utils::OpenHandle(*obj4);
1872 // TODO(verwaest): Restore to Constructor4 once supported by the
1873 // heap-snapshot-generator.
1874 CHECK_EQ(
1875 0, StringCmp("Object", i::V8HeapExplorer::GetConstructorName(*js_obj4)));
1876 v8::Local<v8::Object> obj5 = js_global->Get(v8_str("obj5")).As<v8::Object>();
1877 i::Handle<i::JSObject> js_obj5 = v8::Utils::OpenHandle(*obj5);
1878 CHECK_EQ(0, StringCmp(
1879 "Object", i::V8HeapExplorer::GetConstructorName(*js_obj5)));
1880 v8::Local<v8::Object> obj6 = js_global->Get(v8_str("obj6")).As<v8::Object>();
1881 i::Handle<i::JSObject> js_obj6 = v8::Utils::OpenHandle(*obj6);
1882 CHECK_EQ(0, StringCmp(
1883 "Object", i::V8HeapExplorer::GetConstructorName(*js_obj6)));
1884 }
1885
1886
TEST(FastCaseAccessors)1887 TEST(FastCaseAccessors) {
1888 LocalContext env;
1889 v8::HandleScope scope(env->GetIsolate());
1890 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1891
1892 CompileRun("var obj1 = {};\n"
1893 "obj1.__defineGetter__('propWithGetter', function Y() {\n"
1894 " return 42;\n"
1895 "});\n"
1896 "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
1897 " return this.value_ = value;\n"
1898 "});\n");
1899 const v8::HeapSnapshot* snapshot =
1900 heap_profiler->TakeHeapSnapshot(v8_str("fastCaseAccessors"));
1901 CHECK(ValidateSnapshot(snapshot));
1902
1903 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1904 CHECK_NE(NULL, global);
1905 const v8::HeapGraphNode* obj1 =
1906 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
1907 CHECK_NE(NULL, obj1);
1908 const v8::HeapGraphNode* func;
1909 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithGetter");
1910 CHECK_NE(NULL, func);
1911 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithGetter");
1912 CHECK_EQ(NULL, func);
1913 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithSetter");
1914 CHECK_NE(NULL, func);
1915 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithSetter");
1916 CHECK_EQ(NULL, func);
1917 }
1918
1919
TEST(SlowCaseAccessors)1920 TEST(SlowCaseAccessors) {
1921 LocalContext env;
1922 v8::HandleScope scope(env->GetIsolate());
1923 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1924
1925 CompileRun("var obj1 = {};\n"
1926 "for (var i = 0; i < 100; ++i) obj1['z' + i] = {};"
1927 "obj1.__defineGetter__('propWithGetter', function Y() {\n"
1928 " return 42;\n"
1929 "});\n"
1930 "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
1931 " return this.value_ = value;\n"
1932 "});\n");
1933 const v8::HeapSnapshot* snapshot =
1934 heap_profiler->TakeHeapSnapshot(v8_str("slowCaseAccessors"));
1935 CHECK(ValidateSnapshot(snapshot));
1936
1937 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1938 CHECK_NE(NULL, global);
1939 const v8::HeapGraphNode* obj1 =
1940 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
1941 CHECK_NE(NULL, obj1);
1942 const v8::HeapGraphNode* func;
1943 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithGetter");
1944 CHECK_NE(NULL, func);
1945 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithGetter");
1946 CHECK_EQ(NULL, func);
1947 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithSetter");
1948 CHECK_NE(NULL, func);
1949 func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithSetter");
1950 CHECK_EQ(NULL, func);
1951 }
1952
1953
TEST(HiddenPropertiesFastCase)1954 TEST(HiddenPropertiesFastCase) {
1955 LocalContext env;
1956 v8::HandleScope scope(env->GetIsolate());
1957 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1958
1959 CompileRun(
1960 "function C(x) { this.a = this; this.b = x; }\n"
1961 "c = new C(2012);\n");
1962 const v8::HeapSnapshot* snapshot =
1963 heap_profiler->TakeHeapSnapshot(v8_str("HiddenPropertiesFastCase1"));
1964 CHECK(ValidateSnapshot(snapshot));
1965 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
1966 const v8::HeapGraphNode* c =
1967 GetProperty(global, v8::HeapGraphEdge::kProperty, "c");
1968 CHECK_NE(NULL, c);
1969 const v8::HeapGraphNode* hidden_props =
1970 GetProperty(c, v8::HeapGraphEdge::kInternal, "hidden_properties");
1971 CHECK_EQ(NULL, hidden_props);
1972
1973 v8::Handle<v8::Value> cHandle =
1974 env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "c"));
1975 CHECK(!cHandle.IsEmpty() && cHandle->IsObject());
1976 cHandle->ToObject()->SetHiddenValue(v8_str("key"), v8_str("val"));
1977
1978 snapshot = heap_profiler->TakeHeapSnapshot(
1979 v8_str("HiddenPropertiesFastCase2"));
1980 CHECK(ValidateSnapshot(snapshot));
1981 global = GetGlobalObject(snapshot);
1982 c = GetProperty(global, v8::HeapGraphEdge::kProperty, "c");
1983 CHECK_NE(NULL, c);
1984 hidden_props = GetProperty(c, v8::HeapGraphEdge::kInternal,
1985 "hidden_properties");
1986 CHECK_NE(NULL, hidden_props);
1987 }
1988
1989
TEST(AccessorInfo)1990 TEST(AccessorInfo) {
1991 LocalContext env;
1992 v8::HandleScope scope(env->GetIsolate());
1993 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
1994
1995 CompileRun("function foo(x) { }\n");
1996 const v8::HeapSnapshot* snapshot =
1997 heap_profiler->TakeHeapSnapshot(v8_str("AccessorInfoTest"));
1998 CHECK(ValidateSnapshot(snapshot));
1999 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2000 const v8::HeapGraphNode* foo =
2001 GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
2002 CHECK_NE(NULL, foo);
2003 const v8::HeapGraphNode* map =
2004 GetProperty(foo, v8::HeapGraphEdge::kInternal, "map");
2005 CHECK_NE(NULL, map);
2006 const v8::HeapGraphNode* descriptors =
2007 GetProperty(map, v8::HeapGraphEdge::kInternal, "descriptors");
2008 CHECK_NE(NULL, descriptors);
2009 const v8::HeapGraphNode* length_name =
2010 GetProperty(descriptors, v8::HeapGraphEdge::kInternal, "2");
2011 CHECK_NE(NULL, length_name);
2012 CHECK_EQ("length", *v8::String::Utf8Value(length_name->GetName()));
2013 const v8::HeapGraphNode* length_accessor =
2014 GetProperty(descriptors, v8::HeapGraphEdge::kInternal, "4");
2015 CHECK_NE(NULL, length_accessor);
2016 CHECK_EQ("system / ExecutableAccessorInfo",
2017 *v8::String::Utf8Value(length_accessor->GetName()));
2018 const v8::HeapGraphNode* name =
2019 GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "name");
2020 CHECK_NE(NULL, name);
2021 const v8::HeapGraphNode* getter =
2022 GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "getter");
2023 CHECK_NE(NULL, getter);
2024 const v8::HeapGraphNode* setter =
2025 GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "setter");
2026 CHECK_NE(NULL, setter);
2027 }
2028
2029
HasWeakEdge(const v8::HeapGraphNode * node)2030 bool HasWeakEdge(const v8::HeapGraphNode* node) {
2031 for (int i = 0; i < node->GetChildrenCount(); ++i) {
2032 const v8::HeapGraphEdge* handle_edge = node->GetChild(i);
2033 if (handle_edge->GetType() == v8::HeapGraphEdge::kWeak) return true;
2034 }
2035 return false;
2036 }
2037
2038
HasWeakGlobalHandle()2039 bool HasWeakGlobalHandle() {
2040 v8::Isolate* isolate = CcTest::isolate();
2041 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2042 const v8::HeapSnapshot* snapshot =
2043 heap_profiler->TakeHeapSnapshot(v8_str("weaks"));
2044 CHECK(ValidateSnapshot(snapshot));
2045 const v8::HeapGraphNode* gc_roots = GetNode(
2046 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
2047 CHECK_NE(NULL, gc_roots);
2048 const v8::HeapGraphNode* global_handles = GetNode(
2049 gc_roots, v8::HeapGraphNode::kSynthetic, "(Global handles)");
2050 CHECK_NE(NULL, global_handles);
2051 return HasWeakEdge(global_handles);
2052 }
2053
2054
PersistentHandleCallback(const v8::WeakCallbackData<v8::Object,v8::Persistent<v8::Object>> & data)2055 static void PersistentHandleCallback(
2056 const v8::WeakCallbackData<v8::Object, v8::Persistent<v8::Object> >& data) {
2057 data.GetParameter()->Reset();
2058 delete data.GetParameter();
2059 }
2060
2061
TEST(WeakGlobalHandle)2062 TEST(WeakGlobalHandle) {
2063 LocalContext env;
2064 v8::HandleScope scope(env->GetIsolate());
2065
2066 CHECK(!HasWeakGlobalHandle());
2067
2068 v8::Persistent<v8::Object> handle(env->GetIsolate(),
2069 v8::Object::New(env->GetIsolate()));
2070 handle.SetWeak(&handle, PersistentHandleCallback);
2071
2072 CHECK(HasWeakGlobalHandle());
2073 }
2074
2075
TEST(SfiAndJsFunctionWeakRefs)2076 TEST(SfiAndJsFunctionWeakRefs) {
2077 LocalContext env;
2078 v8::HandleScope scope(env->GetIsolate());
2079 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2080
2081 CompileRun(
2082 "fun = (function (x) { return function () { return x + 1; } })(1);");
2083 const v8::HeapSnapshot* snapshot =
2084 heap_profiler->TakeHeapSnapshot(v8_str("fun"));
2085 CHECK(ValidateSnapshot(snapshot));
2086 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2087 CHECK_NE(NULL, global);
2088 const v8::HeapGraphNode* fun =
2089 GetProperty(global, v8::HeapGraphEdge::kProperty, "fun");
2090 CHECK(!HasWeakEdge(fun));
2091 const v8::HeapGraphNode* shared =
2092 GetProperty(fun, v8::HeapGraphEdge::kInternal, "shared");
2093 CHECK(!HasWeakEdge(shared));
2094 }
2095
2096
TEST(NoDebugObjectInSnapshot)2097 TEST(NoDebugObjectInSnapshot) {
2098 LocalContext env;
2099 v8::HandleScope scope(env->GetIsolate());
2100 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2101
2102 CHECK(CcTest::i_isolate()->debug()->Load());
2103 CompileRun("foo = {};");
2104 const v8::HeapSnapshot* snapshot =
2105 heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
2106 CHECK(ValidateSnapshot(snapshot));
2107 const v8::HeapGraphNode* root = snapshot->GetRoot();
2108 int globals_count = 0;
2109 for (int i = 0; i < root->GetChildrenCount(); ++i) {
2110 const v8::HeapGraphEdge* edge = root->GetChild(i);
2111 if (edge->GetType() == v8::HeapGraphEdge::kShortcut) {
2112 ++globals_count;
2113 const v8::HeapGraphNode* global = edge->GetToNode();
2114 const v8::HeapGraphNode* foo =
2115 GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
2116 CHECK_NE(NULL, foo);
2117 }
2118 }
2119 CHECK_EQ(1, globals_count);
2120 }
2121
2122
TEST(AllStrongGcRootsHaveNames)2123 TEST(AllStrongGcRootsHaveNames) {
2124 LocalContext env;
2125 v8::HandleScope scope(env->GetIsolate());
2126 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2127
2128 CompileRun("foo = {};");
2129 const v8::HeapSnapshot* snapshot =
2130 heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
2131 CHECK(ValidateSnapshot(snapshot));
2132 const v8::HeapGraphNode* gc_roots = GetNode(
2133 snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
2134 CHECK_NE(NULL, gc_roots);
2135 const v8::HeapGraphNode* strong_roots = GetNode(
2136 gc_roots, v8::HeapGraphNode::kSynthetic, "(Strong roots)");
2137 CHECK_NE(NULL, strong_roots);
2138 for (int i = 0; i < strong_roots->GetChildrenCount(); ++i) {
2139 const v8::HeapGraphEdge* edge = strong_roots->GetChild(i);
2140 CHECK_EQ(v8::HeapGraphEdge::kInternal, edge->GetType());
2141 v8::String::Utf8Value name(edge->GetName());
2142 CHECK(isalpha(**name));
2143 }
2144 }
2145
2146
TEST(NoRefsToNonEssentialEntries)2147 TEST(NoRefsToNonEssentialEntries) {
2148 LocalContext env;
2149 v8::HandleScope scope(env->GetIsolate());
2150 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2151 CompileRun("global_object = {};\n");
2152 const v8::HeapSnapshot* snapshot =
2153 heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
2154 CHECK(ValidateSnapshot(snapshot));
2155 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2156 const v8::HeapGraphNode* global_object =
2157 GetProperty(global, v8::HeapGraphEdge::kProperty, "global_object");
2158 CHECK_NE(NULL, global_object);
2159 const v8::HeapGraphNode* properties =
2160 GetProperty(global_object, v8::HeapGraphEdge::kInternal, "properties");
2161 CHECK_EQ(NULL, properties);
2162 const v8::HeapGraphNode* elements =
2163 GetProperty(global_object, v8::HeapGraphEdge::kInternal, "elements");
2164 CHECK_EQ(NULL, elements);
2165 }
2166
2167
TEST(MapHasDescriptorsAndTransitions)2168 TEST(MapHasDescriptorsAndTransitions) {
2169 LocalContext env;
2170 v8::HandleScope scope(env->GetIsolate());
2171 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2172 CompileRun("obj = { a: 10 };\n");
2173 const v8::HeapSnapshot* snapshot =
2174 heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
2175 CHECK(ValidateSnapshot(snapshot));
2176 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2177 const v8::HeapGraphNode* global_object =
2178 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj");
2179 CHECK_NE(NULL, global_object);
2180
2181 const v8::HeapGraphNode* map =
2182 GetProperty(global_object, v8::HeapGraphEdge::kInternal, "map");
2183 CHECK_NE(NULL, map);
2184 const v8::HeapGraphNode* own_descriptors = GetProperty(
2185 map, v8::HeapGraphEdge::kInternal, "descriptors");
2186 CHECK_NE(NULL, own_descriptors);
2187 const v8::HeapGraphNode* own_transitions = GetProperty(
2188 map, v8::HeapGraphEdge::kInternal, "transitions");
2189 CHECK_EQ(NULL, own_transitions);
2190 }
2191
2192
TEST(ManyLocalsInSharedContext)2193 TEST(ManyLocalsInSharedContext) {
2194 LocalContext env;
2195 v8::HandleScope scope(env->GetIsolate());
2196 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2197 int num_objects = 6000;
2198 CompileRun(
2199 "var n = 6000;"
2200 "var result = [];"
2201 "result.push('(function outer() {');"
2202 "for (var i = 0; i < n; i++) {"
2203 " var f = 'function f_' + i + '() { ';"
2204 " if (i > 0)"
2205 " f += 'f_' + (i - 1) + '();';"
2206 " f += ' }';"
2207 " result.push(f);"
2208 "}"
2209 "result.push('return f_' + (n - 1) + ';');"
2210 "result.push('})()');"
2211 "var ok = eval(result.join('\\n'));");
2212 const v8::HeapSnapshot* snapshot =
2213 heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
2214 CHECK(ValidateSnapshot(snapshot));
2215
2216 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2217 CHECK_NE(NULL, global);
2218 const v8::HeapGraphNode* ok_object =
2219 GetProperty(global, v8::HeapGraphEdge::kProperty, "ok");
2220 CHECK_NE(NULL, ok_object);
2221 const v8::HeapGraphNode* context_object =
2222 GetProperty(ok_object, v8::HeapGraphEdge::kInternal, "context");
2223 CHECK_NE(NULL, context_object);
2224 // Check the objects are not duplicated in the context.
2225 CHECK_EQ(v8::internal::Context::MIN_CONTEXT_SLOTS + num_objects - 1,
2226 context_object->GetChildrenCount());
2227 // Check all the objects have got their names.
2228 // ... well check just every 15th because otherwise it's too slow in debug.
2229 for (int i = 0; i < num_objects - 1; i += 15) {
2230 i::EmbeddedVector<char, 100> var_name;
2231 i::SNPrintF(var_name, "f_%d", i);
2232 const v8::HeapGraphNode* f_object = GetProperty(
2233 context_object, v8::HeapGraphEdge::kContextVariable, var_name.start());
2234 CHECK_NE(NULL, f_object);
2235 }
2236 }
2237
2238
TEST(AllocationSitesAreVisible)2239 TEST(AllocationSitesAreVisible) {
2240 LocalContext env;
2241 v8::Isolate* isolate = env->GetIsolate();
2242 v8::HandleScope scope(isolate);
2243 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2244 CompileRun(
2245 "fun = function () { var a = [3, 2, 1]; return a; }\n"
2246 "fun();");
2247 const v8::HeapSnapshot* snapshot =
2248 heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
2249 CHECK(ValidateSnapshot(snapshot));
2250
2251 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2252 CHECK_NE(NULL, global);
2253 const v8::HeapGraphNode* fun_code =
2254 GetProperty(global, v8::HeapGraphEdge::kProperty, "fun");
2255 CHECK_NE(NULL, fun_code);
2256 const v8::HeapGraphNode* literals =
2257 GetProperty(fun_code, v8::HeapGraphEdge::kInternal, "literals");
2258 CHECK_NE(NULL, literals);
2259 CHECK_EQ(v8::HeapGraphNode::kArray, literals->GetType());
2260 CHECK_EQ(2, literals->GetChildrenCount());
2261
2262 // The second value in the literals array should be the boilerplate,
2263 // after an AllocationSite.
2264 const v8::HeapGraphEdge* prop = literals->GetChild(1);
2265 const v8::HeapGraphNode* allocation_site = prop->GetToNode();
2266 v8::String::Utf8Value name(allocation_site->GetName());
2267 CHECK_EQ("system / AllocationSite", *name);
2268 const v8::HeapGraphNode* transition_info =
2269 GetProperty(allocation_site, v8::HeapGraphEdge::kInternal,
2270 "transition_info");
2271 CHECK_NE(NULL, transition_info);
2272
2273 const v8::HeapGraphNode* elements =
2274 GetProperty(transition_info, v8::HeapGraphEdge::kInternal,
2275 "elements");
2276 CHECK_NE(NULL, elements);
2277 CHECK_EQ(v8::HeapGraphNode::kArray, elements->GetType());
2278 CHECK_EQ(v8::internal::FixedArray::SizeFor(3),
2279 static_cast<int>(elements->GetShallowSize()));
2280
2281 v8::Handle<v8::Value> array_val =
2282 heap_profiler->FindObjectById(transition_info->GetId());
2283 CHECK(array_val->IsArray());
2284 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(array_val);
2285 // Verify the array is "a" in the code above.
2286 CHECK_EQ(3, array->Length());
2287 CHECK_EQ(v8::Integer::New(isolate, 3),
2288 array->Get(v8::Integer::New(isolate, 0)));
2289 CHECK_EQ(v8::Integer::New(isolate, 2),
2290 array->Get(v8::Integer::New(isolate, 1)));
2291 CHECK_EQ(v8::Integer::New(isolate, 1),
2292 array->Get(v8::Integer::New(isolate, 2)));
2293 }
2294
2295
TEST(JSFunctionHasCodeLink)2296 TEST(JSFunctionHasCodeLink) {
2297 LocalContext env;
2298 v8::HandleScope scope(env->GetIsolate());
2299 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2300 CompileRun("function foo(x, y) { return x + y; }\n");
2301 const v8::HeapSnapshot* snapshot =
2302 heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
2303 CHECK(ValidateSnapshot(snapshot));
2304 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2305 const v8::HeapGraphNode* foo_func =
2306 GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
2307 CHECK_NE(NULL, foo_func);
2308 const v8::HeapGraphNode* code =
2309 GetProperty(foo_func, v8::HeapGraphEdge::kInternal, "code");
2310 CHECK_NE(NULL, code);
2311 }
2312
2313
GetNodeByPath(const v8::HeapSnapshot * snapshot,const char * path[],int depth)2314 static const v8::HeapGraphNode* GetNodeByPath(const v8::HeapSnapshot* snapshot,
2315 const char* path[],
2316 int depth) {
2317 const v8::HeapGraphNode* node = snapshot->GetRoot();
2318 for (int current_depth = 0; current_depth < depth; ++current_depth) {
2319 int i, count = node->GetChildrenCount();
2320 for (i = 0; i < count; ++i) {
2321 const v8::HeapGraphEdge* edge = node->GetChild(i);
2322 const v8::HeapGraphNode* to_node = edge->GetToNode();
2323 v8::String::Utf8Value edge_name(edge->GetName());
2324 v8::String::Utf8Value node_name(to_node->GetName());
2325 i::EmbeddedVector<char, 100> name;
2326 i::SNPrintF(name, "%s::%s", *edge_name, *node_name);
2327 if (strstr(name.start(), path[current_depth])) {
2328 node = to_node;
2329 break;
2330 }
2331 }
2332 if (i == count) return NULL;
2333 }
2334 return node;
2335 }
2336
2337
TEST(CheckCodeNames)2338 TEST(CheckCodeNames) {
2339 LocalContext env;
2340 v8::HandleScope scope(env->GetIsolate());
2341 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2342 CompileRun("var a = 1.1;");
2343 const v8::HeapSnapshot* snapshot =
2344 heap_profiler->TakeHeapSnapshot(v8_str("CheckCodeNames"));
2345 CHECK(ValidateSnapshot(snapshot));
2346
2347 const char* stub_path[] = {
2348 "::(GC roots)",
2349 "::(Strong roots)",
2350 "code_stubs::",
2351 "::(ArraySingleArgumentConstructorStub code)"
2352 };
2353 const v8::HeapGraphNode* node = GetNodeByPath(snapshot,
2354 stub_path, arraysize(stub_path));
2355 CHECK_NE(NULL, node);
2356
2357 const char* builtin_path1[] = {
2358 "::(GC roots)",
2359 "::(Builtins)",
2360 "::(KeyedLoadIC_Generic builtin)"
2361 };
2362 node = GetNodeByPath(snapshot, builtin_path1, arraysize(builtin_path1));
2363 CHECK_NE(NULL, node);
2364
2365 const char* builtin_path2[] = {"::(GC roots)", "::(Builtins)",
2366 "::(CompileLazy builtin)"};
2367 node = GetNodeByPath(snapshot, builtin_path2, arraysize(builtin_path2));
2368 CHECK_NE(NULL, node);
2369 v8::String::Utf8Value node_name(node->GetName());
2370 CHECK_EQ("(CompileLazy builtin)", *node_name);
2371 }
2372
2373
2374 static const char* record_trace_tree_source =
2375 "var topFunctions = [];\n"
2376 "var global = this;\n"
2377 "function generateFunctions(width, depth) {\n"
2378 " var script = [];\n"
2379 " for (var i = 0; i < width; i++) {\n"
2380 " for (var j = 0; j < depth; j++) {\n"
2381 " script.push('function f_' + i + '_' + j + '(x) {\\n');\n"
2382 " script.push(' try {\\n');\n"
2383 " if (j < depth-2) {\n"
2384 " script.push(' return f_' + i + '_' + (j+1) + '(x+1);\\n');\n"
2385 " } else if (j == depth - 2) {\n"
2386 " script.push(' return new f_' + i + '_' + (depth - 1) + '();\\n');\n"
2387 " } else if (j == depth - 1) {\n"
2388 " script.push(' this.ts = Date.now();\\n');\n"
2389 " }\n"
2390 " script.push(' } catch (e) {}\\n');\n"
2391 " script.push('}\\n');\n"
2392 " \n"
2393 " }\n"
2394 " }\n"
2395 " var script = script.join('');\n"
2396 " // throw script;\n"
2397 " global.eval(script);\n"
2398 " for (var i = 0; i < width; i++) {\n"
2399 " topFunctions.push(this['f_' + i + '_0']);\n"
2400 " }\n"
2401 "}\n"
2402 "\n"
2403 "var width = 3;\n"
2404 "var depth = 3;\n"
2405 "generateFunctions(width, depth);\n"
2406 "var instances = [];\n"
2407 "function start() {\n"
2408 " for (var i = 0; i < width; i++) {\n"
2409 " instances.push(topFunctions[i](0));\n"
2410 " }\n"
2411 "}\n"
2412 "\n"
2413 "for (var i = 0; i < 100; i++) start();\n";
2414
2415
FindNode(AllocationTracker * tracker,const Vector<const char * > & names)2416 static AllocationTraceNode* FindNode(
2417 AllocationTracker* tracker, const Vector<const char*>& names) {
2418 AllocationTraceNode* node = tracker->trace_tree()->root();
2419 for (int i = 0; node != NULL && i < names.length(); i++) {
2420 const char* name = names[i];
2421 Vector<AllocationTraceNode*> children = node->children();
2422 node = NULL;
2423 for (int j = 0; j < children.length(); j++) {
2424 unsigned index = children[j]->function_info_index();
2425 AllocationTracker::FunctionInfo* info =
2426 tracker->function_info_list()[index];
2427 if (info && strcmp(info->name, name) == 0) {
2428 node = children[j];
2429 break;
2430 }
2431 }
2432 }
2433 return node;
2434 }
2435
2436
TEST(ArrayGrowLeftTrim)2437 TEST(ArrayGrowLeftTrim) {
2438 LocalContext env;
2439 v8::HandleScope scope(env->GetIsolate());
2440 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2441 heap_profiler->StartTrackingHeapObjects(true);
2442
2443 CompileRun(
2444 "var a = [];\n"
2445 "for (var i = 0; i < 5; ++i)\n"
2446 " a[i] = i;\n"
2447 "for (var i = 0; i < 3; ++i)\n"
2448 " a.shift();\n");
2449
2450 const char* names[] = {""};
2451 AllocationTracker* tracker =
2452 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2453 CHECK_NE(NULL, tracker);
2454 // Resolve all function locations.
2455 tracker->PrepareForSerialization();
2456 // Print for better diagnostics in case of failure.
2457 tracker->trace_tree()->Print(tracker);
2458
2459 AllocationTraceNode* node =
2460 FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2461 CHECK_NE(NULL, node);
2462 CHECK_GE(node->allocation_count(), 2);
2463 CHECK_GE(node->allocation_size(), 4 * 5);
2464 heap_profiler->StopTrackingHeapObjects();
2465 }
2466
2467
TEST(TrackHeapAllocations)2468 TEST(TrackHeapAllocations) {
2469 v8::HandleScope scope(v8::Isolate::GetCurrent());
2470 LocalContext env;
2471
2472 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2473 heap_profiler->StartTrackingHeapObjects(true);
2474
2475 CompileRun(record_trace_tree_source);
2476
2477 AllocationTracker* tracker =
2478 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2479 CHECK_NE(NULL, tracker);
2480 // Resolve all function locations.
2481 tracker->PrepareForSerialization();
2482 // Print for better diagnostics in case of failure.
2483 tracker->trace_tree()->Print(tracker);
2484
2485 const char* names[] = {"", "start", "f_0_0", "f_0_1", "f_0_2"};
2486 AllocationTraceNode* node =
2487 FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2488 CHECK_NE(NULL, node);
2489 CHECK_GE(node->allocation_count(), 100);
2490 CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2491 heap_profiler->StopTrackingHeapObjects();
2492 }
2493
2494
2495 static const char* inline_heap_allocation_source =
2496 "function f_0(x) {\n"
2497 " return f_1(x+1);\n"
2498 "}\n"
2499 "%NeverOptimizeFunction(f_0);\n"
2500 "function f_1(x) {\n"
2501 " return new f_2(x+1);\n"
2502 "}\n"
2503 "function f_2(x) {\n"
2504 " this.foo = x;\n"
2505 "}\n"
2506 "var instances = [];\n"
2507 "function start() {\n"
2508 " instances.push(f_0(0));\n"
2509 "}\n"
2510 "\n"
2511 "for (var i = 0; i < 100; i++) start();\n";
2512
2513
TEST(TrackBumpPointerAllocations)2514 TEST(TrackBumpPointerAllocations) {
2515 i::FLAG_allow_natives_syntax = true;
2516 v8::HandleScope scope(v8::Isolate::GetCurrent());
2517 LocalContext env;
2518
2519 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2520 const char* names[] = {"", "start", "f_0", "f_1"};
2521 // First check that normally all allocations are recorded.
2522 {
2523 heap_profiler->StartTrackingHeapObjects(true);
2524
2525 CompileRun(inline_heap_allocation_source);
2526
2527 AllocationTracker* tracker =
2528 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2529 CHECK_NE(NULL, tracker);
2530 // Resolve all function locations.
2531 tracker->PrepareForSerialization();
2532 // Print for better diagnostics in case of failure.
2533 tracker->trace_tree()->Print(tracker);
2534
2535 AllocationTraceNode* node =
2536 FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2537 CHECK_NE(NULL, node);
2538 CHECK_GE(node->allocation_count(), 100);
2539 CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2540 heap_profiler->StopTrackingHeapObjects();
2541 }
2542
2543 {
2544 heap_profiler->StartTrackingHeapObjects(true);
2545
2546 // Now check that not all allocations are tracked if we manually reenable
2547 // inline allocations.
2548 CHECK(CcTest::heap()->inline_allocation_disabled());
2549 CcTest::heap()->EnableInlineAllocation();
2550
2551 CompileRun(inline_heap_allocation_source);
2552
2553 AllocationTracker* tracker =
2554 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2555 CHECK_NE(NULL, tracker);
2556 // Resolve all function locations.
2557 tracker->PrepareForSerialization();
2558 // Print for better diagnostics in case of failure.
2559 tracker->trace_tree()->Print(tracker);
2560
2561 AllocationTraceNode* node =
2562 FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2563 CHECK_NE(NULL, node);
2564 CHECK_LT(node->allocation_count(), 100);
2565
2566 CcTest::heap()->DisableInlineAllocation();
2567 heap_profiler->StopTrackingHeapObjects();
2568 }
2569 }
2570
2571
TEST(TrackV8ApiAllocation)2572 TEST(TrackV8ApiAllocation) {
2573 v8::HandleScope scope(v8::Isolate::GetCurrent());
2574 LocalContext env;
2575
2576 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2577 const char* names[] = { "(V8 API)" };
2578 heap_profiler->StartTrackingHeapObjects(true);
2579
2580 v8::Handle<v8::Object> o1 = v8::Object::New(env->GetIsolate());
2581 o1->Clone();
2582
2583 AllocationTracker* tracker =
2584 reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
2585 CHECK_NE(NULL, tracker);
2586 // Resolve all function locations.
2587 tracker->PrepareForSerialization();
2588 // Print for better diagnostics in case of failure.
2589 tracker->trace_tree()->Print(tracker);
2590
2591 AllocationTraceNode* node =
2592 FindNode(tracker, Vector<const char*>(names, arraysize(names)));
2593 CHECK_NE(NULL, node);
2594 CHECK_GE(node->allocation_count(), 2);
2595 CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
2596 heap_profiler->StopTrackingHeapObjects();
2597 }
2598
2599
TEST(ArrayBufferAndArrayBufferView)2600 TEST(ArrayBufferAndArrayBufferView) {
2601 LocalContext env;
2602 v8::HandleScope scope(env->GetIsolate());
2603 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2604 CompileRun("arr1 = new Uint32Array(100);\n");
2605 const v8::HeapSnapshot* snapshot =
2606 heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
2607 CHECK(ValidateSnapshot(snapshot));
2608 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2609 const v8::HeapGraphNode* arr1_obj =
2610 GetProperty(global, v8::HeapGraphEdge::kProperty, "arr1");
2611 CHECK_NE(NULL, arr1_obj);
2612 const v8::HeapGraphNode* arr1_buffer =
2613 GetProperty(arr1_obj, v8::HeapGraphEdge::kInternal, "buffer");
2614 CHECK_NE(NULL, arr1_buffer);
2615 const v8::HeapGraphNode* first_view =
2616 GetProperty(arr1_buffer, v8::HeapGraphEdge::kWeak, "weak_first_view");
2617 CHECK_NE(NULL, first_view);
2618 const v8::HeapGraphNode* backing_store =
2619 GetProperty(arr1_buffer, v8::HeapGraphEdge::kInternal, "backing_store");
2620 CHECK_NE(NULL, backing_store);
2621 CHECK_EQ(400, static_cast<int>(backing_store->GetShallowSize()));
2622 }
2623
2624
GetRetainersCount(const v8::HeapSnapshot * snapshot,const v8::HeapGraphNode * node)2625 static int GetRetainersCount(const v8::HeapSnapshot* snapshot,
2626 const v8::HeapGraphNode* node) {
2627 int count = 0;
2628 for (int i = 0, l = snapshot->GetNodesCount(); i < l; ++i) {
2629 const v8::HeapGraphNode* parent = snapshot->GetNode(i);
2630 for (int j = 0, l2 = parent->GetChildrenCount(); j < l2; ++j) {
2631 if (parent->GetChild(j)->GetToNode() == node) {
2632 ++count;
2633 }
2634 }
2635 }
2636 return count;
2637 }
2638
2639
TEST(ArrayBufferSharedBackingStore)2640 TEST(ArrayBufferSharedBackingStore) {
2641 LocalContext env;
2642 v8::Isolate* isolate = env->GetIsolate();
2643 v8::HandleScope handle_scope(isolate);
2644 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2645
2646 v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 1024);
2647 CHECK_EQ(1024, static_cast<int>(ab->ByteLength()));
2648 CHECK(!ab->IsExternal());
2649 v8::ArrayBuffer::Contents ab_contents = ab->Externalize();
2650 CHECK(ab->IsExternal());
2651
2652 CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
2653 void* data = ab_contents.Data();
2654 DCHECK(data != NULL);
2655 v8::Local<v8::ArrayBuffer> ab2 =
2656 v8::ArrayBuffer::New(isolate, data, ab_contents.ByteLength());
2657 CHECK(ab2->IsExternal());
2658 env->Global()->Set(v8_str("ab1"), ab);
2659 env->Global()->Set(v8_str("ab2"), ab2);
2660
2661 v8::Handle<v8::Value> result = CompileRun("ab2.byteLength");
2662 CHECK_EQ(1024, result->Int32Value());
2663
2664 const v8::HeapSnapshot* snapshot =
2665 heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
2666 CHECK(ValidateSnapshot(snapshot));
2667 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2668 const v8::HeapGraphNode* ab1_node =
2669 GetProperty(global, v8::HeapGraphEdge::kProperty, "ab1");
2670 CHECK_NE(NULL, ab1_node);
2671 const v8::HeapGraphNode* ab1_data =
2672 GetProperty(ab1_node, v8::HeapGraphEdge::kInternal, "backing_store");
2673 CHECK_NE(NULL, ab1_data);
2674 const v8::HeapGraphNode* ab2_node =
2675 GetProperty(global, v8::HeapGraphEdge::kProperty, "ab2");
2676 CHECK_NE(NULL, ab2_node);
2677 const v8::HeapGraphNode* ab2_data =
2678 GetProperty(ab2_node, v8::HeapGraphEdge::kInternal, "backing_store");
2679 CHECK_NE(NULL, ab2_data);
2680 CHECK_EQ(ab1_data, ab2_data);
2681 CHECK_EQ(2, GetRetainersCount(snapshot, ab1_data));
2682 free(data);
2683 }
2684
2685
TEST(BoxObject)2686 TEST(BoxObject) {
2687 v8::Isolate* isolate = CcTest::isolate();
2688 v8::HandleScope scope(isolate);
2689 LocalContext env;
2690 v8::Handle<v8::Object> global_proxy = env->Global();
2691 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
2692
2693 i::Factory* factory = CcTest::i_isolate()->factory();
2694 i::Handle<i::String> string = factory->NewStringFromStaticChars("string");
2695 i::Handle<i::Object> box = factory->NewBox(string);
2696 global->Set(0, v8::ToApiHandle<v8::Object>(box));
2697
2698 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
2699 const v8::HeapSnapshot* snapshot =
2700 heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
2701 CHECK(ValidateSnapshot(snapshot));
2702 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
2703 const v8::HeapGraphNode* box_node =
2704 GetProperty(global_node, v8::HeapGraphEdge::kElement, "0");
2705 CHECK_NE(NULL, box_node);
2706 v8::String::Utf8Value box_node_name(box_node->GetName());
2707 CHECK_EQ("system / Box", *box_node_name);
2708 const v8::HeapGraphNode* box_value =
2709 GetProperty(box_node, v8::HeapGraphEdge::kInternal, "value");
2710 CHECK_NE(NULL, box_value);
2711 }
2712
2713
TEST(WeakContainers)2714 TEST(WeakContainers) {
2715 i::FLAG_allow_natives_syntax = true;
2716 LocalContext env;
2717 v8::HandleScope scope(env->GetIsolate());
2718 if (!CcTest::i_isolate()->use_crankshaft()) return;
2719 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
2720 CompileRun(
2721 "function foo(a) { return a.x; }\n"
2722 "obj = {x : 123};\n"
2723 "foo(obj);\n"
2724 "foo(obj);\n"
2725 "%OptimizeFunctionOnNextCall(foo);\n"
2726 "foo(obj);\n");
2727 const v8::HeapSnapshot* snapshot =
2728 heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
2729 CHECK(ValidateSnapshot(snapshot));
2730 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
2731 const v8::HeapGraphNode* obj =
2732 GetProperty(global, v8::HeapGraphEdge::kProperty, "obj");
2733 CHECK_NE(NULL, obj);
2734 const v8::HeapGraphNode* map =
2735 GetProperty(obj, v8::HeapGraphEdge::kInternal, "map");
2736 CHECK_NE(NULL, map);
2737 const v8::HeapGraphNode* dependent_code =
2738 GetProperty(map, v8::HeapGraphEdge::kInternal, "dependent_code");
2739 if (!dependent_code) return;
2740 int count = dependent_code->GetChildrenCount();
2741 CHECK_NE(0, count);
2742 for (int i = 0; i < count; ++i) {
2743 const v8::HeapGraphEdge* prop = dependent_code->GetChild(i);
2744 CHECK_EQ(v8::HeapGraphEdge::kWeak, prop->GetType());
2745 }
2746 }
2747
2748
ToAddress(int n)2749 static inline i::Address ToAddress(int n) {
2750 return reinterpret_cast<i::Address>(n);
2751 }
2752
2753
TEST(AddressToTraceMap)2754 TEST(AddressToTraceMap) {
2755 i::AddressToTraceMap map;
2756
2757 CHECK_EQ(0, map.GetTraceNodeId(ToAddress(150)));
2758
2759 // [0x100, 0x200) -> 1
2760 map.AddRange(ToAddress(0x100), 0x100, 1U);
2761 CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x50)));
2762 CHECK_EQ(1, map.GetTraceNodeId(ToAddress(0x100)));
2763 CHECK_EQ(1, map.GetTraceNodeId(ToAddress(0x150)));
2764 CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x100 + 0x100)));
2765 CHECK_EQ(1, static_cast<int>(map.size()));
2766
2767 // [0x100, 0x200) -> 1, [0x200, 0x300) -> 2
2768 map.AddRange(ToAddress(0x200), 0x100, 2U);
2769 CHECK_EQ(2, map.GetTraceNodeId(ToAddress(0x2a0)));
2770 CHECK_EQ(2, static_cast<int>(map.size()));
2771
2772 // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2
2773 map.AddRange(ToAddress(0x180), 0x100, 3U);
2774 CHECK_EQ(1, map.GetTraceNodeId(ToAddress(0x17F)));
2775 CHECK_EQ(2, map.GetTraceNodeId(ToAddress(0x280)));
2776 CHECK_EQ(3, map.GetTraceNodeId(ToAddress(0x180)));
2777 CHECK_EQ(3, static_cast<int>(map.size()));
2778
2779 // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2,
2780 // [0x400, 0x500) -> 4
2781 map.AddRange(ToAddress(0x400), 0x100, 4U);
2782 CHECK_EQ(1, map.GetTraceNodeId(ToAddress(0x17F)));
2783 CHECK_EQ(2, map.GetTraceNodeId(ToAddress(0x280)));
2784 CHECK_EQ(3, map.GetTraceNodeId(ToAddress(0x180)));
2785 CHECK_EQ(4, map.GetTraceNodeId(ToAddress(0x450)));
2786 CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x500)));
2787 CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x350)));
2788 CHECK_EQ(4, static_cast<int>(map.size()));
2789
2790 // [0x100, 0x180) -> 1, [0x180, 0x200) -> 3, [0x200, 0x600) -> 5
2791 map.AddRange(ToAddress(0x200), 0x400, 5U);
2792 CHECK_EQ(5, map.GetTraceNodeId(ToAddress(0x200)));
2793 CHECK_EQ(5, map.GetTraceNodeId(ToAddress(0x400)));
2794 CHECK_EQ(3, static_cast<int>(map.size()));
2795
2796 // [0x100, 0x180) -> 1, [0x180, 0x200) -> 7, [0x200, 0x600) ->5
2797 map.AddRange(ToAddress(0x180), 0x80, 6U);
2798 map.AddRange(ToAddress(0x180), 0x80, 7U);
2799 CHECK_EQ(7, map.GetTraceNodeId(ToAddress(0x180)));
2800 CHECK_EQ(5, map.GetTraceNodeId(ToAddress(0x200)));
2801 CHECK_EQ(3, static_cast<int>(map.size()));
2802
2803 map.Clear();
2804 CHECK_EQ(0, static_cast<int>(map.size()));
2805 CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x400)));
2806 }
2807