blob: 143a23cbd551115bd727e295dd8ec94f3d281440 [file] [log] [blame]
Ben Murdoch257744e2011-11-30 15:57:28 +00001// Copyright 2011 the V8 project authors. All rights reserved.
Steve Blocka7e24c12009-10-30 11:49:00 +00002//
3// Tests for heap profiler
4
Steve Blocka7e24c12009-10-30 11:49:00 +00005#include "v8.h"
Ben Murdoch257744e2011-11-30 15:57:28 +00006
7#include "cctest.h"
Steve Blocka7e24c12009-10-30 11:49:00 +00008#include "heap-profiler.h"
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009#include "snapshot.h"
Ben Murdoch257744e2011-11-30 15:57:28 +000010#include "utils-inl.h"
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011#include "../include/v8-profiler.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000012
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010013namespace {
14
15class NamedEntriesDetector {
16 public:
17 NamedEntriesDetector()
Russell Brenner90bac252010-11-18 13:33:46 -080018 : has_A2(false), has_B2(false), has_C2(false) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010019 }
20
Iain Merrick75681382010-08-19 15:07:18 +010021 void Apply(i::HeapEntry** entry_ptr) {
Iain Merrick75681382010-08-19 15:07:18 +010022 if (IsReachableNodeWithName(*entry_ptr, "A2")) has_A2 = true;
23 if (IsReachableNodeWithName(*entry_ptr, "B2")) has_B2 = true;
24 if (IsReachableNodeWithName(*entry_ptr, "C2")) has_C2 = true;
25 }
26
27 static bool IsReachableNodeWithName(i::HeapEntry* entry, const char* name) {
28 return strcmp(name, entry->name()) == 0 && entry->painted_reachable();
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010029 }
30
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010031 bool has_A2;
32 bool has_B2;
33 bool has_C2;
34};
35
36} // namespace
37
38
39static const v8::HeapGraphNode* GetGlobalObject(
40 const v8::HeapSnapshot* snapshot) {
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -080041 CHECK_EQ(2, snapshot->GetRoot()->GetChildrenCount());
42 const v8::HeapGraphNode* global_obj =
43 snapshot->GetRoot()->GetChild(0)->GetToNode();
Ben Murdoch3fb3ca82011-12-02 17:19:32 +000044 CHECK_EQ(0, strncmp("Object", const_cast<i::HeapEntry*>(
45 reinterpret_cast<const i::HeapEntry*>(global_obj))->name(), 6));
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -080046 return global_obj;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010047}
48
49
50static const v8::HeapGraphNode* GetProperty(const v8::HeapGraphNode* node,
51 v8::HeapGraphEdge::Type type,
52 const char* name) {
53 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
54 const v8::HeapGraphEdge* prop = node->GetChild(i);
55 v8::String::AsciiValue prop_name(prop->GetName());
56 if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
57 return prop->GetToNode();
58 }
59 return NULL;
60}
61
62
63static bool HasString(const v8::HeapGraphNode* node, const char* contents) {
64 for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
65 const v8::HeapGraphEdge* prop = node->GetChild(i);
66 const v8::HeapGraphNode* node = prop->GetToNode();
Iain Merrick75681382010-08-19 15:07:18 +010067 if (node->GetType() == v8::HeapGraphNode::kString) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010068 v8::String::AsciiValue node_name(node->GetName());
69 if (strcmp(contents, *node_name) == 0) return true;
70 }
71 }
72 return false;
73}
74
75
76TEST(HeapSnapshot) {
77 v8::HandleScope scope;
Ben Murdoch3bec4d22010-07-22 14:51:16 +010078 LocalContext env2;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010079
Ben Murdochf87a2032010-10-22 12:50:53 +010080 CompileRun(
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010081 "function A2() {}\n"
82 "function B2(x) { return function() { return typeof x; }; }\n"
83 "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n"
84 "var a2 = new A2();\n"
85 "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n"
86 "var c2 = new C2(a2);");
87 const v8::HeapSnapshot* snapshot_env2 =
Ben Murdoch69a99ed2011-11-30 16:03:39 +000088 v8::HeapProfiler::TakeSnapshot(v8_str("env2"));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010089 i::HeapSnapshot* i_snapshot_env2 =
90 const_cast<i::HeapSnapshot*>(
91 reinterpret_cast<const i::HeapSnapshot*>(snapshot_env2));
Iain Merrick75681382010-08-19 15:07:18 +010092 const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2);
93 // Paint all nodes reachable from global object.
94 i_snapshot_env2->ClearPaint();
95 const_cast<i::HeapEntry*>(
96 reinterpret_cast<const i::HeapEntry*>(global_env2))->PaintAllReachable();
97
Russell Brenner90bac252010-11-18 13:33:46 -080098 // Verify, that JS global object of env2 has '..2' properties.
Iain Merrick75681382010-08-19 15:07:18 +010099 const v8::HeapGraphNode* a2_node =
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800100 GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "a2");
Iain Merrick75681382010-08-19 15:07:18 +0100101 CHECK_NE(NULL, a2_node);
102 CHECK_NE(
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800103 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_1"));
Iain Merrick75681382010-08-19 15:07:18 +0100104 CHECK_NE(
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800105 NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_2"));
106 CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "c2"));
Iain Merrick75681382010-08-19 15:07:18 +0100107
Iain Merrick75681382010-08-19 15:07:18 +0100108 NamedEntriesDetector det;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100109 i_snapshot_env2->IterateEntries(&det);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100110 CHECK(det.has_A2);
111 CHECK(det.has_B2);
112 CHECK(det.has_C2);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100113}
114
115
Iain Merrick75681382010-08-19 15:07:18 +0100116TEST(HeapSnapshotObjectSizes) {
117 v8::HandleScope scope;
118 LocalContext env;
119
120 // -a-> X1 --a
121 // x -b-> X2 <-|
Ben Murdochf87a2032010-10-22 12:50:53 +0100122 CompileRun(
Iain Merrick75681382010-08-19 15:07:18 +0100123 "function X(a, b) { this.a = a; this.b = b; }\n"
124 "x = new X(new X(), new X());\n"
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800125 "(function() { x.a.a = x.b; })();");
Iain Merrick75681382010-08-19 15:07:18 +0100126 const v8::HeapSnapshot* snapshot =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000127 v8::HeapProfiler::TakeSnapshot(v8_str("sizes"));
Iain Merrick75681382010-08-19 15:07:18 +0100128 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
129 const v8::HeapGraphNode* x =
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800130 GetProperty(global, v8::HeapGraphEdge::kShortcut, "x");
Iain Merrick75681382010-08-19 15:07:18 +0100131 CHECK_NE(NULL, x);
Iain Merrick75681382010-08-19 15:07:18 +0100132 const v8::HeapGraphNode* x1 =
133 GetProperty(x, v8::HeapGraphEdge::kProperty, "a");
134 CHECK_NE(NULL, x1);
135 const v8::HeapGraphNode* x2 =
136 GetProperty(x, v8::HeapGraphEdge::kProperty, "b");
137 CHECK_NE(NULL, x2);
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800138
139 // Test approximate sizes.
140 CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize(false));
141 CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize(false));
142 CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize(false));
143 // Test exact sizes.
144 CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize(true));
145 CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize(true));
146 CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize(true));
Iain Merrick75681382010-08-19 15:07:18 +0100147}
148
149
150TEST(HeapSnapshotEntryChildren) {
151 v8::HandleScope scope;
152 LocalContext env;
153
Ben Murdochf87a2032010-10-22 12:50:53 +0100154 CompileRun(
Iain Merrick75681382010-08-19 15:07:18 +0100155 "function A() { }\n"
156 "a = new A;");
157 const v8::HeapSnapshot* snapshot =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000158 v8::HeapProfiler::TakeSnapshot(v8_str("children"));
Iain Merrick75681382010-08-19 15:07:18 +0100159 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
160 for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) {
161 const v8::HeapGraphEdge* prop = global->GetChild(i);
162 CHECK_EQ(global, prop->GetFromNode());
163 }
164 const v8::HeapGraphNode* a =
165 GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
166 CHECK_NE(NULL, a);
167 for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) {
168 const v8::HeapGraphEdge* prop = a->GetChild(i);
169 CHECK_EQ(a, prop->GetFromNode());
170 }
171}
172
173
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100174TEST(HeapSnapshotCodeObjects) {
175 v8::HandleScope scope;
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100176 LocalContext env;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100177
Ben Murdochf87a2032010-10-22 12:50:53 +0100178 CompileRun(
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100179 "function lazy(x) { return x - 1; }\n"
180 "function compiled(x) { return x + 1; }\n"
Steve Block791712a2010-08-27 10:21:07 +0100181 "var anonymous = (function() { return function() { return 0; } })();\n"
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100182 "compiled(1)");
183 const v8::HeapSnapshot* snapshot =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000184 v8::HeapProfiler::TakeSnapshot(v8_str("code"));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100185
186 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
187 const v8::HeapGraphNode* compiled =
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800188 GetProperty(global, v8::HeapGraphEdge::kShortcut, "compiled");
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100189 CHECK_NE(NULL, compiled);
Iain Merrick75681382010-08-19 15:07:18 +0100190 CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType());
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100191 const v8::HeapGraphNode* lazy =
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800192 GetProperty(global, v8::HeapGraphEdge::kShortcut, "lazy");
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100193 CHECK_NE(NULL, lazy);
Iain Merrick75681382010-08-19 15:07:18 +0100194 CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
Steve Block791712a2010-08-27 10:21:07 +0100195 const v8::HeapGraphNode* anonymous =
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800196 GetProperty(global, v8::HeapGraphEdge::kShortcut, "anonymous");
Steve Block791712a2010-08-27 10:21:07 +0100197 CHECK_NE(NULL, anonymous);
198 CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
199 v8::String::AsciiValue anonymous_name(anonymous->GetName());
Ben Murdochf87a2032010-10-22 12:50:53 +0100200 CHECK_EQ("", *anonymous_name);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100201
202 // Find references to code.
203 const v8::HeapGraphNode* compiled_code =
Ben Murdoch8b112d22011-06-08 16:22:53 +0100204 GetProperty(compiled, v8::HeapGraphEdge::kInternal, "shared");
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100205 CHECK_NE(NULL, compiled_code);
206 const v8::HeapGraphNode* lazy_code =
Ben Murdoch8b112d22011-06-08 16:22:53 +0100207 GetProperty(lazy, v8::HeapGraphEdge::kInternal, "shared");
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100208 CHECK_NE(NULL, lazy_code);
209
210 // Verify that non-compiled code doesn't contain references to "x"
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100211 // literal, while compiled code does. The scope info is stored in FixedArray
212 // objects attached to the SharedFunctionInfo.
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100213 bool compiled_references_x = false, lazy_references_x = false;
214 for (int i = 0, count = compiled_code->GetChildrenCount(); i < count; ++i) {
215 const v8::HeapGraphEdge* prop = compiled_code->GetChild(i);
216 const v8::HeapGraphNode* node = prop->GetToNode();
Iain Merrick75681382010-08-19 15:07:18 +0100217 if (node->GetType() == v8::HeapGraphNode::kArray) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100218 if (HasString(node, "x")) {
219 compiled_references_x = true;
220 break;
221 }
222 }
223 }
224 for (int i = 0, count = lazy_code->GetChildrenCount(); i < count; ++i) {
225 const v8::HeapGraphEdge* prop = lazy_code->GetChild(i);
226 const v8::HeapGraphNode* node = prop->GetToNode();
Iain Merrick75681382010-08-19 15:07:18 +0100227 if (node->GetType() == v8::HeapGraphNode::kArray) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100228 if (HasString(node, "x")) {
229 lazy_references_x = true;
230 break;
231 }
232 }
233 }
234 CHECK(compiled_references_x);
235 CHECK(!lazy_references_x);
236}
237
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100238
Ben Murdochf87a2032010-10-22 12:50:53 +0100239TEST(HeapSnapshotHeapNumbers) {
240 v8::HandleScope scope;
241 LocalContext env;
242 CompileRun(
243 "a = 1; // a is Smi\n"
244 "b = 2.5; // b is HeapNumber");
245 const v8::HeapSnapshot* snapshot =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000246 v8::HeapProfiler::TakeSnapshot(v8_str("numbers"));
Ben Murdochf87a2032010-10-22 12:50:53 +0100247 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800248 CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kShortcut, "a"));
Ben Murdochf87a2032010-10-22 12:50:53 +0100249 const v8::HeapGraphNode* b =
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800250 GetProperty(global, v8::HeapGraphEdge::kShortcut, "b");
Ben Murdochf87a2032010-10-22 12:50:53 +0100251 CHECK_NE(NULL, b);
252 CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType());
253}
254
255
256TEST(HeapSnapshotInternalReferences) {
257 v8::HandleScope scope;
258 v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
259 global_template->SetInternalFieldCount(2);
260 LocalContext env(NULL, global_template);
261 v8::Handle<v8::Object> global_proxy = env->Global();
262 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
263 CHECK_EQ(2, global->InternalFieldCount());
264 v8::Local<v8::Object> obj = v8::Object::New();
265 global->SetInternalField(0, v8_num(17));
266 global->SetInternalField(1, obj);
267 const v8::HeapSnapshot* snapshot =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000268 v8::HeapProfiler::TakeSnapshot(v8_str("internals"));
Ben Murdochf87a2032010-10-22 12:50:53 +0100269 const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
270 // The first reference will not present, because it's a Smi.
271 CHECK_EQ(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0"));
272 // The second reference is to an object.
273 CHECK_NE(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "1"));
274}
275
276
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100277// Trying to introduce a check helper for uint64_t causes many
278// overloading ambiguities, so it seems easier just to cast
279// them to a signed type.
280#define CHECK_EQ_UINT64_T(a, b) \
281 CHECK_EQ(static_cast<int64_t>(a), static_cast<int64_t>(b))
Iain Merrick75681382010-08-19 15:07:18 +0100282#define CHECK_NE_UINT64_T(a, b) \
283 CHECK((a) != (b)) // NOLINT
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100284
285TEST(HeapEntryIdsAndGC) {
286 v8::HandleScope scope;
287 LocalContext env;
288
Ben Murdochf87a2032010-10-22 12:50:53 +0100289 CompileRun(
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100290 "function A() {}\n"
291 "function B(x) { this.x = x; }\n"
292 "var a = new A();\n"
293 "var b = new B(a);");
294 const v8::HeapSnapshot* snapshot1 =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000295 v8::HeapProfiler::TakeSnapshot(v8_str("s1"));
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100296
Steve Block44f0eee2011-05-26 01:26:41 +0100297 HEAP->CollectAllGarbage(true); // Enforce compaction.
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100298
299 const v8::HeapSnapshot* snapshot2 =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000300 v8::HeapProfiler::TakeSnapshot(v8_str("s2"));
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100301
302 const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
303 const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
304 CHECK_NE_UINT64_T(0, global1->GetId());
305 CHECK_EQ_UINT64_T(global1->GetId(), global2->GetId());
306 const v8::HeapGraphNode* A1 =
Iain Merrick75681382010-08-19 15:07:18 +0100307 GetProperty(global1, v8::HeapGraphEdge::kProperty, "A");
308 CHECK_NE(NULL, A1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100309 const v8::HeapGraphNode* A2 =
Iain Merrick75681382010-08-19 15:07:18 +0100310 GetProperty(global2, v8::HeapGraphEdge::kProperty, "A");
311 CHECK_NE(NULL, A2);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100312 CHECK_NE_UINT64_T(0, A1->GetId());
313 CHECK_EQ_UINT64_T(A1->GetId(), A2->GetId());
314 const v8::HeapGraphNode* B1 =
Iain Merrick75681382010-08-19 15:07:18 +0100315 GetProperty(global1, v8::HeapGraphEdge::kProperty, "B");
316 CHECK_NE(NULL, B1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100317 const v8::HeapGraphNode* B2 =
Iain Merrick75681382010-08-19 15:07:18 +0100318 GetProperty(global2, v8::HeapGraphEdge::kProperty, "B");
319 CHECK_NE(NULL, B2);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100320 CHECK_NE_UINT64_T(0, B1->GetId());
321 CHECK_EQ_UINT64_T(B1->GetId(), B2->GetId());
322 const v8::HeapGraphNode* a1 =
Iain Merrick75681382010-08-19 15:07:18 +0100323 GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
324 CHECK_NE(NULL, a1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100325 const v8::HeapGraphNode* a2 =
Iain Merrick75681382010-08-19 15:07:18 +0100326 GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
327 CHECK_NE(NULL, a2);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100328 CHECK_NE_UINT64_T(0, a1->GetId());
329 CHECK_EQ_UINT64_T(a1->GetId(), a2->GetId());
330 const v8::HeapGraphNode* b1 =
Iain Merrick75681382010-08-19 15:07:18 +0100331 GetProperty(global1, v8::HeapGraphEdge::kProperty, "b");
332 CHECK_NE(NULL, b1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100333 const v8::HeapGraphNode* b2 =
Iain Merrick75681382010-08-19 15:07:18 +0100334 GetProperty(global2, v8::HeapGraphEdge::kProperty, "b");
335 CHECK_NE(NULL, b2);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100336 CHECK_NE_UINT64_T(0, b1->GetId());
337 CHECK_EQ_UINT64_T(b1->GetId(), b2->GetId());
338}
339
340
Ben Murdochf87a2032010-10-22 12:50:53 +0100341TEST(HeapSnapshotRootPreservedAfterSorting) {
342 v8::HandleScope scope;
343 LocalContext env;
344 const v8::HeapSnapshot* snapshot =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000345 v8::HeapProfiler::TakeSnapshot(v8_str("s"));
Ben Murdochf87a2032010-10-22 12:50:53 +0100346 const v8::HeapGraphNode* root1 = snapshot->GetRoot();
347 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>(
348 snapshot))->GetSortedEntriesList();
349 const v8::HeapGraphNode* root2 = snapshot->GetRoot();
350 CHECK_EQ(root1, root2);
351}
352
353
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800354TEST(HeapEntryDominator) {
355 // The graph looks like this:
356 //
357 // -> node1
358 // a |^
359 // -> node5 ba
360 // a v|
361 // node6 -> node2
362 // b a |^
363 // -> node4 ba
364 // b v|
365 // -> node3
366 //
367 // The dominator for all nodes is node6.
368
369 v8::HandleScope scope;
370 LocalContext env;
371
372 CompileRun(
373 "function X(a, b) { this.a = a; this.b = b; }\n"
374 "node6 = new X(new X(new X()), new X(new X(),new X()));\n"
375 "(function(){\n"
376 "node6.a.a.b = node6.b.a; // node1 -> node2\n"
377 "node6.b.a.a = node6.a.a; // node2 -> node1\n"
378 "node6.b.a.b = node6.b.b; // node2 -> node3\n"
379 "node6.b.b.a = node6.b.a; // node3 -> node2\n"
380 "})();");
381
382 const v8::HeapSnapshot* snapshot =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000383 v8::HeapProfiler::TakeSnapshot(v8_str("dominators"));
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800384
385 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
386 CHECK_NE(NULL, global);
387 const v8::HeapGraphNode* node6 =
388 GetProperty(global, v8::HeapGraphEdge::kShortcut, "node6");
389 CHECK_NE(NULL, node6);
390 const v8::HeapGraphNode* node5 =
391 GetProperty(node6, v8::HeapGraphEdge::kProperty, "a");
392 CHECK_NE(NULL, node5);
393 const v8::HeapGraphNode* node4 =
394 GetProperty(node6, v8::HeapGraphEdge::kProperty, "b");
395 CHECK_NE(NULL, node4);
396 const v8::HeapGraphNode* node3 =
397 GetProperty(node4, v8::HeapGraphEdge::kProperty, "b");
398 CHECK_NE(NULL, node3);
399 const v8::HeapGraphNode* node2 =
400 GetProperty(node4, v8::HeapGraphEdge::kProperty, "a");
401 CHECK_NE(NULL, node2);
402 const v8::HeapGraphNode* node1 =
403 GetProperty(node5, v8::HeapGraphEdge::kProperty, "a");
404 CHECK_NE(NULL, node1);
405
406 CHECK_EQ(node6, node1->GetDominatorNode());
407 CHECK_EQ(node6, node2->GetDominatorNode());
408 CHECK_EQ(node6, node3->GetDominatorNode());
409 CHECK_EQ(node6, node4->GetDominatorNode());
410 CHECK_EQ(node6, node5->GetDominatorNode());
411}
412
413
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100414namespace {
415
416class TestJSONStream : public v8::OutputStream {
417 public:
418 TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {}
419 explicit TestJSONStream(int abort_countdown)
420 : eos_signaled_(0), abort_countdown_(abort_countdown) {}
421 virtual ~TestJSONStream() {}
422 virtual void EndOfStream() { ++eos_signaled_; }
423 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
424 if (abort_countdown_ > 0) --abort_countdown_;
425 if (abort_countdown_ == 0) return kAbort;
426 CHECK_GT(chars_written, 0);
427 i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
428 memcpy(chunk.start(), buffer, chars_written);
429 return kContinue;
430 }
431 void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
432 int eos_signaled() { return eos_signaled_; }
433 int size() { return buffer_.size(); }
434 private:
435 i::Collector<char> buffer_;
436 int eos_signaled_;
437 int abort_countdown_;
438};
439
440class AsciiResource: public v8::String::ExternalAsciiStringResource {
441 public:
442 explicit AsciiResource(i::Vector<char> string): data_(string.start()) {
443 length_ = string.length();
444 }
445 virtual const char* data() const { return data_; }
446 virtual size_t length() const { return length_; }
447 private:
448 const char* data_;
449 size_t length_;
450};
451
452} // namespace
453
454TEST(HeapSnapshotJSONSerialization) {
455 v8::HandleScope scope;
456 LocalContext env;
457
458#define STRING_LITERAL_FOR_TEST \
459 "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
Ben Murdochf87a2032010-10-22 12:50:53 +0100460 CompileRun(
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100461 "function A(s) { this.s = s; }\n"
462 "function B(x) { this.x = x; }\n"
463 "var a = new A(" STRING_LITERAL_FOR_TEST ");\n"
464 "var b = new B(a);");
465 const v8::HeapSnapshot* snapshot =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000466 v8::HeapProfiler::TakeSnapshot(v8_str("json"));
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100467 TestJSONStream stream;
468 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
469 CHECK_GT(stream.size(), 0);
470 CHECK_EQ(1, stream.eos_signaled());
471 i::ScopedVector<char> json(stream.size());
472 stream.WriteTo(json);
473
474 // Verify that snapshot string is valid JSON.
475 AsciiResource json_res(json);
476 v8::Local<v8::String> json_string = v8::String::NewExternal(&json_res);
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000477 env->Global()->Set(v8_str("json_snapshot"), json_string);
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100478 v8::Local<v8::Value> snapshot_parse_result = CompileRun(
479 "var parsed = JSON.parse(json_snapshot); true;");
480 CHECK(!snapshot_parse_result.IsEmpty());
481
482 // Verify that snapshot object has required fields.
483 v8::Local<v8::Object> parsed_snapshot =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000484 env->Global()->Get(v8_str("parsed"))->ToObject();
485 CHECK(parsed_snapshot->Has(v8_str("snapshot")));
486 CHECK(parsed_snapshot->Has(v8_str("nodes")));
487 CHECK(parsed_snapshot->Has(v8_str("strings")));
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100488
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100489 // Get node and edge "member" offsets.
490 v8::Local<v8::Value> meta_analysis_result = CompileRun(
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800491 "var parsed_meta = parsed.nodes[0];\n"
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100492 "var children_count_offset ="
493 " parsed_meta.fields.indexOf('children_count');\n"
494 "var children_offset ="
495 " parsed_meta.fields.indexOf('children');\n"
496 "var children_meta ="
497 " parsed_meta.types[children_offset];\n"
498 "var child_fields_count = children_meta.fields.length;\n"
499 "var child_type_offset ="
500 " children_meta.fields.indexOf('type');\n"
501 "var child_name_offset ="
502 " children_meta.fields.indexOf('name_or_index');\n"
503 "var child_to_node_offset ="
504 " children_meta.fields.indexOf('to_node');\n"
505 "var property_type ="
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800506 " children_meta.types[child_type_offset].indexOf('property');\n"
507 "var shortcut_type ="
508 " children_meta.types[child_type_offset].indexOf('shortcut');");
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100509 CHECK(!meta_analysis_result.IsEmpty());
510
511 // A helper function for processing encoded nodes.
512 CompileRun(
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800513 "function GetChildPosByProperty(pos, prop_name, prop_type) {\n"
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100514 " var nodes = parsed.nodes;\n"
515 " var strings = parsed.strings;\n"
516 " for (var i = 0,\n"
517 " count = nodes[pos + children_count_offset] * child_fields_count;\n"
518 " i < count; i += child_fields_count) {\n"
519 " var child_pos = pos + children_offset + i;\n"
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800520 " if (nodes[child_pos + child_type_offset] === prop_type\n"
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100521 " && strings[nodes[child_pos + child_name_offset]] === prop_name)\n"
522 " return nodes[child_pos + child_to_node_offset];\n"
523 " }\n"
524 " return null;\n"
525 "}\n");
526 // Get the string index using the path: <root> -> <global>.b.x.s
527 v8::Local<v8::Value> string_obj_pos_val = CompileRun(
528 "GetChildPosByProperty(\n"
529 " GetChildPosByProperty(\n"
530 " GetChildPosByProperty("
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800531 " parsed.nodes[1 + children_offset + child_to_node_offset],"
532 " \"b\",shortcut_type),\n"
533 " \"x\", property_type),"
534 " \"s\", property_type)");
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100535 CHECK(!string_obj_pos_val.IsEmpty());
536 int string_obj_pos =
537 static_cast<int>(string_obj_pos_val->ToNumber()->Value());
538 v8::Local<v8::Object> nodes_array =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000539 parsed_snapshot->Get(v8_str("nodes"))->ToObject();
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100540 int string_index = static_cast<int>(
541 nodes_array->Get(string_obj_pos + 1)->ToNumber()->Value());
542 CHECK_GT(string_index, 0);
543 v8::Local<v8::Object> strings_array =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000544 parsed_snapshot->Get(v8_str("strings"))->ToObject();
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100545 v8::Local<v8::String> string = strings_array->Get(string_index)->ToString();
546 v8::Local<v8::String> ref_string =
547 CompileRun(STRING_LITERAL_FOR_TEST)->ToString();
548#undef STRING_LITERAL_FOR_TEST
549 CHECK_EQ(*v8::String::Utf8Value(ref_string),
550 *v8::String::Utf8Value(string));
551}
552
553
554TEST(HeapSnapshotJSONSerializationAborting) {
555 v8::HandleScope scope;
556 LocalContext env;
557 const v8::HeapSnapshot* snapshot =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000558 v8::HeapProfiler::TakeSnapshot(v8_str("abort"));
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100559 TestJSONStream stream(5);
560 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
561 CHECK_GT(stream.size(), 0);
562 CHECK_EQ(0, stream.eos_signaled());
563}
564
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800565
Ben Murdochb0fe1622011-05-05 13:52:32 +0100566TEST(HeapSnapshotGetNodeById) {
567 v8::HandleScope scope;
568 LocalContext env;
569
570 const v8::HeapSnapshot* snapshot =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000571 v8::HeapProfiler::TakeSnapshot(v8_str("id"));
Ben Murdochb0fe1622011-05-05 13:52:32 +0100572 const v8::HeapGraphNode* root = snapshot->GetRoot();
573 CHECK_EQ(root, snapshot->GetNodeById(root->GetId()));
574 for (int i = 0, count = root->GetChildrenCount(); i < count; ++i) {
575 const v8::HeapGraphEdge* prop = root->GetChild(i);
576 CHECK_EQ(
577 prop->GetToNode(), snapshot->GetNodeById(prop->GetToNode()->GetId()));
578 }
579 // Check a big id, which should not exist yet.
580 CHECK_EQ(NULL, snapshot->GetNodeById(0x1000000UL));
581}
582
583
584namespace {
585
586class TestActivityControl : public v8::ActivityControl {
587 public:
588 explicit TestActivityControl(int abort_count)
589 : done_(0), total_(0), abort_count_(abort_count) {}
590 ControlOption ReportProgressValue(int done, int total) {
591 done_ = done;
592 total_ = total;
593 return --abort_count_ != 0 ? kContinue : kAbort;
594 }
595 int done() { return done_; }
596 int total() { return total_; }
597
598 private:
599 int done_;
600 int total_;
601 int abort_count_;
602};
603}
604
605TEST(TakeHeapSnapshotAborting) {
606 v8::HandleScope scope;
607 LocalContext env;
608
609 const int snapshots_count = v8::HeapProfiler::GetSnapshotsCount();
610 TestActivityControl aborting_control(3);
611 const v8::HeapSnapshot* no_snapshot =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000612 v8::HeapProfiler::TakeSnapshot(v8_str("abort"),
Ben Murdochb0fe1622011-05-05 13:52:32 +0100613 v8::HeapSnapshot::kFull,
614 &aborting_control);
615 CHECK_EQ(NULL, no_snapshot);
616 CHECK_EQ(snapshots_count, v8::HeapProfiler::GetSnapshotsCount());
617 CHECK_GT(aborting_control.total(), aborting_control.done());
618
619 TestActivityControl control(-1); // Don't abort.
620 const v8::HeapSnapshot* snapshot =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000621 v8::HeapProfiler::TakeSnapshot(v8_str("full"),
Ben Murdochb0fe1622011-05-05 13:52:32 +0100622 v8::HeapSnapshot::kFull,
623 &control);
624 CHECK_NE(NULL, snapshot);
625 CHECK_EQ(snapshots_count + 1, v8::HeapProfiler::GetSnapshotsCount());
626 CHECK_EQ(control.total(), control.done());
627 CHECK_GT(control.total(), 0);
628}
629
Steve Block44f0eee2011-05-26 01:26:41 +0100630
631namespace {
632
633class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
634 public:
635 TestRetainedObjectInfo(int hash,
636 const char* label,
637 intptr_t element_count = -1,
638 intptr_t size = -1)
639 : disposed_(false),
640 hash_(hash),
641 label_(label),
642 element_count_(element_count),
643 size_(size) {
644 instances.Add(this);
645 }
646 virtual ~TestRetainedObjectInfo() {}
647 virtual void Dispose() {
648 CHECK(!disposed_);
649 disposed_ = true;
650 }
651 virtual bool IsEquivalent(RetainedObjectInfo* other) {
652 return GetHash() == other->GetHash();
653 }
654 virtual intptr_t GetHash() { return hash_; }
655 virtual const char* GetLabel() { return label_; }
656 virtual intptr_t GetElementCount() { return element_count_; }
657 virtual intptr_t GetSizeInBytes() { return size_; }
658 bool disposed() { return disposed_; }
659
660 static v8::RetainedObjectInfo* WrapperInfoCallback(
661 uint16_t class_id, v8::Handle<v8::Value> wrapper) {
662 if (class_id == 1) {
663 if (wrapper->IsString()) {
664 v8::String::AsciiValue ascii(wrapper);
665 if (strcmp(*ascii, "AAA") == 0)
666 return new TestRetainedObjectInfo(1, "aaa", 100);
667 else if (strcmp(*ascii, "BBB") == 0)
668 return new TestRetainedObjectInfo(1, "aaa", 100);
669 }
670 } else if (class_id == 2) {
671 if (wrapper->IsString()) {
672 v8::String::AsciiValue ascii(wrapper);
673 if (strcmp(*ascii, "CCC") == 0)
674 return new TestRetainedObjectInfo(2, "ccc");
675 }
676 }
677 CHECK(false);
678 return NULL;
679 }
680
681 static i::List<TestRetainedObjectInfo*> instances;
682
683 private:
684 bool disposed_;
685 int category_;
686 int hash_;
687 const char* label_;
688 intptr_t element_count_;
689 intptr_t size_;
690};
691
692
693i::List<TestRetainedObjectInfo*> TestRetainedObjectInfo::instances;
694}
695
696
697static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent,
698 v8::HeapGraphNode::Type type,
699 const char* name) {
700 for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) {
701 const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode();
702 if (node->GetType() == type && strcmp(name,
703 const_cast<i::HeapEntry*>(
704 reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) {
705 return node;
706 }
707 }
708 return NULL;
709}
710
711
712TEST(HeapSnapshotRetainedObjectInfo) {
713 v8::HandleScope scope;
714 LocalContext env;
715
716 v8::HeapProfiler::DefineWrapperClass(
717 1, TestRetainedObjectInfo::WrapperInfoCallback);
718 v8::HeapProfiler::DefineWrapperClass(
719 2, TestRetainedObjectInfo::WrapperInfoCallback);
720 v8::Persistent<v8::String> p_AAA =
721 v8::Persistent<v8::String>::New(v8_str("AAA"));
722 p_AAA.SetWrapperClassId(1);
723 v8::Persistent<v8::String> p_BBB =
724 v8::Persistent<v8::String>::New(v8_str("BBB"));
725 p_BBB.SetWrapperClassId(1);
726 v8::Persistent<v8::String> p_CCC =
727 v8::Persistent<v8::String>::New(v8_str("CCC"));
728 p_CCC.SetWrapperClassId(2);
729 CHECK_EQ(0, TestRetainedObjectInfo::instances.length());
730 const v8::HeapSnapshot* snapshot =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000731 v8::HeapProfiler::TakeSnapshot(v8_str("retained"));
Steve Block44f0eee2011-05-26 01:26:41 +0100732
733 CHECK_EQ(3, TestRetainedObjectInfo::instances.length());
734 for (int i = 0; i < TestRetainedObjectInfo::instances.length(); ++i) {
735 CHECK(TestRetainedObjectInfo::instances[i]->disposed());
736 delete TestRetainedObjectInfo::instances[i];
737 }
738
739 const v8::HeapGraphNode* natives = GetNode(
740 snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(Native objects)");
741 CHECK_NE(NULL, natives);
742 CHECK_EQ(2, natives->GetChildrenCount());
743 const v8::HeapGraphNode* aaa = GetNode(
744 natives, v8::HeapGraphNode::kNative, "aaa / 100 entries");
745 CHECK_NE(NULL, aaa);
746 const v8::HeapGraphNode* ccc = GetNode(
747 natives, v8::HeapGraphNode::kNative, "ccc");
748 CHECK_NE(NULL, ccc);
749
750 CHECK_EQ(2, aaa->GetChildrenCount());
751 const v8::HeapGraphNode* n_AAA = GetNode(
752 aaa, v8::HeapGraphNode::kString, "AAA");
753 CHECK_NE(NULL, n_AAA);
754 const v8::HeapGraphNode* n_BBB = GetNode(
755 aaa, v8::HeapGraphNode::kString, "BBB");
756 CHECK_NE(NULL, n_BBB);
757 CHECK_EQ(1, ccc->GetChildrenCount());
758 const v8::HeapGraphNode* n_CCC = GetNode(
759 ccc, v8::HeapGraphNode::kString, "CCC");
760 CHECK_NE(NULL, n_CCC);
761
Ben Murdoch8b112d22011-06-08 16:22:53 +0100762 CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "native"));
763 CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "native"));
764 CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "native"));
Steve Block44f0eee2011-05-26 01:26:41 +0100765}
766
767
768TEST(DeleteAllHeapSnapshots) {
769 v8::HandleScope scope;
770 LocalContext env;
771
772 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
773 v8::HeapProfiler::DeleteAllSnapshots();
774 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000775 CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8_str("1")));
Steve Block44f0eee2011-05-26 01:26:41 +0100776 CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
777 v8::HeapProfiler::DeleteAllSnapshots();
778 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000779 CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8_str("1")));
780 CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8_str("2")));
Steve Block44f0eee2011-05-26 01:26:41 +0100781 CHECK_EQ(2, v8::HeapProfiler::GetSnapshotsCount());
782 v8::HeapProfiler::DeleteAllSnapshots();
783 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
784}
785
786
787TEST(DeleteHeapSnapshot) {
788 v8::HandleScope scope;
789 LocalContext env;
790
791 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
792 const v8::HeapSnapshot* s1 =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000793 v8::HeapProfiler::TakeSnapshot(v8_str("1"));
Steve Block44f0eee2011-05-26 01:26:41 +0100794 CHECK_NE(NULL, s1);
795 CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
796 unsigned uid1 = s1->GetUid();
797 CHECK_EQ(s1, v8::HeapProfiler::FindSnapshot(uid1));
798 const_cast<v8::HeapSnapshot*>(s1)->Delete();
799 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
800 CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid1));
801
802 const v8::HeapSnapshot* s2 =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000803 v8::HeapProfiler::TakeSnapshot(v8_str("2"));
Steve Block44f0eee2011-05-26 01:26:41 +0100804 CHECK_NE(NULL, s2);
805 CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
806 unsigned uid2 = s2->GetUid();
807 CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid2));
808 CHECK_EQ(s2, v8::HeapProfiler::FindSnapshot(uid2));
809 const v8::HeapSnapshot* s3 =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000810 v8::HeapProfiler::TakeSnapshot(v8_str("3"));
Steve Block44f0eee2011-05-26 01:26:41 +0100811 CHECK_NE(NULL, s3);
812 CHECK_EQ(2, v8::HeapProfiler::GetSnapshotsCount());
813 unsigned uid3 = s3->GetUid();
814 CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid3));
815 CHECK_EQ(s3, v8::HeapProfiler::FindSnapshot(uid3));
816 const_cast<v8::HeapSnapshot*>(s2)->Delete();
817 CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
818 CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid2));
819 CHECK_EQ(s3, v8::HeapProfiler::FindSnapshot(uid3));
820 const_cast<v8::HeapSnapshot*>(s3)->Delete();
821 CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
822 CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid3));
823}
824
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000825
826TEST(DocumentURL) {
827 v8::HandleScope scope;
828 LocalContext env;
829
830 CompileRun("document = { URL:\"abcdefgh\" };");
831
832 const v8::HeapSnapshot* snapshot =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000833 v8::HeapProfiler::TakeSnapshot(v8_str("document"));
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000834 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
835 CHECK_NE(NULL, global);
836 CHECK_EQ("Object / abcdefgh",
837 const_cast<i::HeapEntry*>(
838 reinterpret_cast<const i::HeapEntry*>(global))->name());
839}
840
841
842TEST(DocumentWithException) {
843 v8::HandleScope scope;
844 LocalContext env;
845
846 CompileRun(
847 "this.__defineGetter__(\"document\", function() { throw new Error(); })");
848 const v8::HeapSnapshot* snapshot =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000849 v8::HeapProfiler::TakeSnapshot(v8_str("document"));
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000850 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
851 CHECK_NE(NULL, global);
852 CHECK_EQ("Object",
853 const_cast<i::HeapEntry*>(
854 reinterpret_cast<const i::HeapEntry*>(global))->name());
855}
856
857
858TEST(DocumentURLWithException) {
859 v8::HandleScope scope;
860 LocalContext env;
861
862 CompileRun(
863 "function URLWithException() {}\n"
864 "URLWithException.prototype = { get URL() { throw new Error(); } };\n"
865 "document = { URL: new URLWithException() };");
866 const v8::HeapSnapshot* snapshot =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000867 v8::HeapProfiler::TakeSnapshot(v8_str("document"));
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000868 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
869 CHECK_NE(NULL, global);
870 CHECK_EQ("Object",
871 const_cast<i::HeapEntry*>(
872 reinterpret_cast<const i::HeapEntry*>(global))->name());
873}
874
875
876TEST(NodesIteration) {
877 v8::HandleScope scope;
878 LocalContext env;
879 const v8::HeapSnapshot* snapshot =
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000880 v8::HeapProfiler::TakeSnapshot(v8_str("iteration"));
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000881 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
882 CHECK_NE(NULL, global);
883 // Verify that we can find this object by iteration.
884 const int nodes_count = snapshot->GetNodesCount();
885 int count = 0;
886 for (int i = 0; i < nodes_count; ++i) {
887 if (snapshot->GetNode(i) == global)
888 ++count;
889 }
890 CHECK_EQ(1, count);
891}
Ben Murdoch69a99ed2011-11-30 16:03:39 +0000892
893
894TEST(GetHeapValue) {
895 v8::HandleScope scope;
896 LocalContext env;
897
898 CompileRun("a = { s_prop: \'value\', n_prop: 0.1 };");
899 const v8::HeapSnapshot* snapshot =
900 v8::HeapProfiler::TakeSnapshot(v8_str("value"));
901 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
902 CHECK(global->GetHeapValue()->IsObject());
903 v8::Local<v8::Object> js_global =
904 env->Global()->GetPrototype().As<v8::Object>();
905 CHECK(js_global == global->GetHeapValue());
906 const v8::HeapGraphNode* obj = GetProperty(
907 global, v8::HeapGraphEdge::kShortcut, "a");
908 CHECK(obj->GetHeapValue()->IsObject());
909 v8::Local<v8::Object> js_obj = js_global->Get(v8_str("a")).As<v8::Object>();
910 CHECK(js_obj == obj->GetHeapValue());
911 const v8::HeapGraphNode* s_prop =
912 GetProperty(obj, v8::HeapGraphEdge::kProperty, "s_prop");
913 v8::Local<v8::String> js_s_prop =
914 js_obj->Get(v8_str("s_prop")).As<v8::String>();
915 CHECK(js_s_prop == s_prop->GetHeapValue());
916 const v8::HeapGraphNode* n_prop =
917 GetProperty(obj, v8::HeapGraphEdge::kProperty, "n_prop");
918 v8::Local<v8::Number> js_n_prop =
919 js_obj->Get(v8_str("n_prop")).As<v8::Number>();
920 CHECK(js_n_prop == n_prop->GetHeapValue());
921}
922
923
924TEST(GetHeapValueForDeletedObject) {
925 v8::HandleScope scope;
926 LocalContext env;
927
928 // It is impossible to delete a global property, so we are about to delete a
929 // property of the "a" object. Also, the "p" object can't be an empty one
930 // because the empty object is static and isn't actually deleted.
931 CompileRun("a = { p: { r: {} } };");
932 const v8::HeapSnapshot* snapshot =
933 v8::HeapProfiler::TakeSnapshot(v8_str("snapshot"));
934 const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
935 const v8::HeapGraphNode* obj = GetProperty(
936 global, v8::HeapGraphEdge::kShortcut, "a");
937 const v8::HeapGraphNode* prop = GetProperty(
938 obj, v8::HeapGraphEdge::kProperty, "p");
939 {
940 // Perform the check inside a nested local scope to avoid creating a
941 // reference to the object we are deleting.
942 v8::HandleScope scope;
943 CHECK(prop->GetHeapValue()->IsObject());
944 }
945 CompileRun("delete a.p;");
946 CHECK(prop->GetHeapValue()->IsUndefined());
947}
948
949
950static int StringCmp(const char* ref, i::String* act) {
951 i::SmartPointer<char> s_act = act->ToCString();
952 int result = strcmp(ref, *s_act);
953 if (result != 0)
954 fprintf(stderr, "Expected: \"%s\", Actual: \"%s\"\n", ref, *s_act);
955 return result;
956}
957
958
959TEST(GetConstructorName) {
960 v8::HandleScope scope;
961 LocalContext env;
962
963 CompileRun(
964 "function Constructor1() {};\n"
965 "var obj1 = new Constructor1();\n"
966 "var Constructor2 = function() {};\n"
967 "var obj2 = new Constructor2();\n"
968 "var obj3 = {};\n"
969 "obj3.constructor = function Constructor3() {};\n"
970 "var obj4 = {};\n"
971 "// Slow properties\n"
972 "for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n"
973 "obj4.constructor = function Constructor4() {};\n"
974 "var obj5 = {};\n"
975 "var obj6 = {};\n"
976 "obj6.constructor = 6;");
977 v8::Local<v8::Object> js_global =
978 env->Global()->GetPrototype().As<v8::Object>();
979 v8::Local<v8::Object> obj1 = js_global->Get(v8_str("obj1")).As<v8::Object>();
980 i::Handle<i::JSObject> js_obj1 = v8::Utils::OpenHandle(*obj1);
981 CHECK_EQ(0, StringCmp(
982 "Constructor1", i::V8HeapExplorer::GetConstructorName(*js_obj1)));
983 v8::Local<v8::Object> obj2 = js_global->Get(v8_str("obj2")).As<v8::Object>();
984 i::Handle<i::JSObject> js_obj2 = v8::Utils::OpenHandle(*obj2);
985 CHECK_EQ(0, StringCmp(
986 "Constructor2", i::V8HeapExplorer::GetConstructorName(*js_obj2)));
987 v8::Local<v8::Object> obj3 = js_global->Get(v8_str("obj3")).As<v8::Object>();
988 i::Handle<i::JSObject> js_obj3 = v8::Utils::OpenHandle(*obj3);
989 CHECK_EQ(0, StringCmp(
990 "Constructor3", i::V8HeapExplorer::GetConstructorName(*js_obj3)));
991 v8::Local<v8::Object> obj4 = js_global->Get(v8_str("obj4")).As<v8::Object>();
992 i::Handle<i::JSObject> js_obj4 = v8::Utils::OpenHandle(*obj4);
993 CHECK_EQ(0, StringCmp(
994 "Constructor4", i::V8HeapExplorer::GetConstructorName(*js_obj4)));
995 v8::Local<v8::Object> obj5 = js_global->Get(v8_str("obj5")).As<v8::Object>();
996 i::Handle<i::JSObject> js_obj5 = v8::Utils::OpenHandle(*obj5);
997 CHECK_EQ(0, StringCmp(
998 "Object", i::V8HeapExplorer::GetConstructorName(*js_obj5)));
999 v8::Local<v8::Object> obj6 = js_global->Get(v8_str("obj6")).As<v8::Object>();
1000 i::Handle<i::JSObject> js_obj6 = v8::Utils::OpenHandle(*obj6);
1001 CHECK_EQ(0, StringCmp(
1002 "Object", i::V8HeapExplorer::GetConstructorName(*js_obj6)));
1003}