Upgrade to 3.29
Update V8 to 3.29.88.17 and update makefiles to support building on
all the relevant platforms.
Bug: 17370214
Change-Id: Ia3407c157fd8d72a93e23d8318ccaf6ecf77fa4e
diff --git a/test/cctest/test-heap-profiler.cc b/test/cctest/test-heap-profiler.cc
index a56f250..8f9b484 100644
--- a/test/cctest/test-heap-profiler.cc
+++ b/test/cctest/test-heap-profiler.cc
@@ -1,14 +1,50 @@
// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Tests for heap profiler
-#include "v8.h"
+#include <ctype.h>
-#include "cctest.h"
-#include "heap-profiler.h"
-#include "snapshot.h"
-#include "utils-inl.h"
-#include "../include/v8-profiler.h"
+#include "src/v8.h"
+
+#include "include/v8-profiler.h"
+#include "src/allocation-tracker.h"
+#include "src/debug.h"
+#include "src/hashmap.h"
+#include "src/heap-profiler.h"
+#include "src/snapshot.h"
+#include "src/utils-inl.h"
+#include "test/cctest/cctest.h"
+
+using i::AllocationTraceNode;
+using i::AllocationTraceTree;
+using i::AllocationTracker;
+using i::HashMap;
+using i::Vector;
namespace {
@@ -24,22 +60,30 @@
if (strcmp(entry->name(), "C2") == 0) has_C2 = true;
}
+ static bool AddressesMatch(void* key1, void* key2) {
+ return key1 == key2;
+ }
+
void CheckAllReachables(i::HeapEntry* root) {
+ i::HashMap visited(AddressesMatch);
i::List<i::HeapEntry*> list(10);
list.Add(root);
- root->paint();
CheckEntry(root);
while (!list.is_empty()) {
i::HeapEntry* entry = list.RemoveLast();
- i::Vector<i::HeapGraphEdge> children = entry->children();
+ i::Vector<i::HeapGraphEdge*> children = entry->children();
for (int i = 0; i < children.length(); ++i) {
- if (children[i].type() == i::HeapGraphEdge::kShortcut) continue;
- i::HeapEntry* child = children[i].to();
- if (!child->painted()) {
- list.Add(child);
- child->paint();
- CheckEntry(child);
- }
+ if (children[i]->type() == i::HeapGraphEdge::kShortcut) continue;
+ i::HeapEntry* child = children[i]->to();
+ i::HashMap::Entry* entry = visited.Lookup(
+ reinterpret_cast<void*>(child),
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(child)),
+ true);
+ if (entry->value)
+ continue;
+ entry->value = reinterpret_cast<void*>(1);
+ list.Add(child);
+ CheckEntry(child);
}
}
}
@@ -55,8 +99,9 @@
static const v8::HeapGraphNode* GetGlobalObject(
const v8::HeapSnapshot* snapshot) {
CHECK_EQ(2, snapshot->GetRoot()->GetChildrenCount());
+ // The 0th-child is (GC Roots), 1st is the user root.
const v8::HeapGraphNode* global_obj =
- snapshot->GetRoot()->GetChild(0)->GetToNode();
+ snapshot->GetRoot()->GetChild(1)->GetToNode();
CHECK_EQ(0, strncmp("Object", const_cast<i::HeapEntry*>(
reinterpret_cast<const i::HeapEntry*>(global_obj))->name(), 6));
return global_obj;
@@ -68,7 +113,7 @@
const char* name) {
for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
const v8::HeapGraphEdge* prop = node->GetChild(i);
- v8::String::AsciiValue prop_name(prop->GetName());
+ v8::String::Utf8Value prop_name(prop->GetName());
if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
return prop->GetToNode();
}
@@ -81,7 +126,7 @@
const v8::HeapGraphEdge* prop = node->GetChild(i);
const v8::HeapGraphNode* node = prop->GetToNode();
if (node->GetType() == v8::HeapGraphNode::kString) {
- v8::String::AsciiValue node_name(node->GetName());
+ v8::String::Utf8Value node_name(node->GetName());
if (strcmp(contents, *node_name) == 0) return true;
}
}
@@ -89,9 +134,47 @@
}
+static bool AddressesMatch(void* key1, void* key2) {
+ return key1 == key2;
+}
+
+
+// Check that snapshot has no unretained entries except root.
+static bool ValidateSnapshot(const v8::HeapSnapshot* snapshot, int depth = 3) {
+ i::HeapSnapshot* heap_snapshot = const_cast<i::HeapSnapshot*>(
+ reinterpret_cast<const i::HeapSnapshot*>(snapshot));
+
+ i::HashMap visited(AddressesMatch);
+ i::List<i::HeapGraphEdge>& edges = heap_snapshot->edges();
+ for (int i = 0; i < edges.length(); ++i) {
+ i::HashMap::Entry* entry = visited.Lookup(
+ reinterpret_cast<void*>(edges[i].to()),
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(edges[i].to())),
+ true);
+ uint32_t ref_count = static_cast<uint32_t>(
+ reinterpret_cast<uintptr_t>(entry->value));
+ entry->value = reinterpret_cast<void*>(ref_count + 1);
+ }
+ uint32_t unretained_entries_count = 0;
+ i::List<i::HeapEntry>& entries = heap_snapshot->entries();
+ for (int i = 0; i < entries.length(); ++i) {
+ i::HashMap::Entry* entry = visited.Lookup(
+ reinterpret_cast<void*>(&entries[i]),
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&entries[i])),
+ false);
+ if (!entry && entries[i].id() != 1) {
+ entries[i].Print("entry with no retainer", "", depth, 0);
+ ++unretained_entries_count;
+ }
+ }
+ return unretained_entries_count == 0;
+}
+
+
TEST(HeapSnapshot) {
- v8::HandleScope scope;
LocalContext env2;
+ v8::HandleScope scope(env2->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env2->GetIsolate()->GetHeapProfiler();
CompileRun(
"function A2() {}\n"
@@ -101,25 +184,21 @@
"var b2_1 = new B2(a2), b2_2 = new B2(a2);\n"
"var c2 = new C2(a2);");
const v8::HeapSnapshot* snapshot_env2 =
- v8::HeapProfiler::TakeSnapshot(v8_str("env2"));
- i::HeapSnapshot* i_snapshot_env2 =
- const_cast<i::HeapSnapshot*>(
- reinterpret_cast<const i::HeapSnapshot*>(snapshot_env2));
+ heap_profiler->TakeHeapSnapshot(v8_str("env2"));
+ CHECK(ValidateSnapshot(snapshot_env2));
const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2);
// Verify, that JS global object of env2 has '..2' properties.
const v8::HeapGraphNode* a2_node =
- GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "a2");
+ GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "a2");
CHECK_NE(NULL, a2_node);
CHECK_NE(
- NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_1"));
+ NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_1"));
CHECK_NE(
- NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_2"));
- CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "c2"));
+ NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_2"));
+ CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "c2"));
- // Paint all nodes reachable from global object.
NamedEntriesDetector det;
- i_snapshot_env2->ClearPaint();
det.CheckAllReachables(const_cast<i::HeapEntry*>(
reinterpret_cast<const i::HeapEntry*>(global_env2)));
CHECK(det.has_A2);
@@ -129,20 +208,23 @@
TEST(HeapSnapshotObjectSizes) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
// -a-> X1 --a
// x -b-> X2 <-|
CompileRun(
"function X(a, b) { this.a = a; this.b = b; }\n"
"x = new X(new X(), new X());\n"
+ "dummy = new X();\n"
"(function() { x.a.a = x.b; })();");
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("sizes"));
+ heap_profiler->TakeHeapSnapshot(v8_str("sizes"));
+ CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
const v8::HeapGraphNode* x =
- GetProperty(global, v8::HeapGraphEdge::kShortcut, "x");
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "x");
CHECK_NE(NULL, x);
const v8::HeapGraphNode* x1 =
GetProperty(x, v8::HeapGraphEdge::kProperty, "a");
@@ -152,26 +234,29 @@
CHECK_NE(NULL, x2);
// Test sizes.
- CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize());
- CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize());
- CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize());
+ CHECK_NE(0, static_cast<int>(x->GetShallowSize()));
+ CHECK_NE(0, static_cast<int>(x1->GetShallowSize()));
+ CHECK_NE(0, static_cast<int>(x2->GetShallowSize()));
}
TEST(BoundFunctionInSnapshot) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
CompileRun(
"function myFunction(a, b) { this.a = a; this.b = b; }\n"
"function AAAAA() {}\n"
"boundFunction = myFunction.bind(new AAAAA(), 20, new Number(12)); \n");
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("sizes"));
+ heap_profiler->TakeHeapSnapshot(v8_str("sizes"));
+ CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
const v8::HeapGraphNode* f =
- GetProperty(global, v8::HeapGraphEdge::kShortcut, "boundFunction");
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "boundFunction");
CHECK(f);
- CHECK_EQ(v8::String::New("native_bind"), f->GetName());
+ CHECK_EQ(v8::String::NewFromUtf8(env->GetIsolate(), "native_bind"),
+ f->GetName());
const v8::HeapGraphNode* bindings =
GetProperty(f, v8::HeapGraphEdge::kInternal, "bindings");
CHECK_NE(NULL, bindings);
@@ -196,14 +281,16 @@
TEST(HeapSnapshotEntryChildren) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
CompileRun(
"function A() { }\n"
"a = new A;");
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("children"));
+ heap_profiler->TakeHeapSnapshot(v8_str("children"));
+ CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) {
const v8::HeapGraphEdge* prop = global->GetChild(i);
@@ -220,8 +307,9 @@
TEST(HeapSnapshotCodeObjects) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
CompileRun(
"function lazy(x) { return x - 1; }\n"
@@ -229,22 +317,23 @@
"var anonymous = (function() { return function() { return 0; } })();\n"
"compiled(1)");
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("code"));
+ heap_profiler->TakeHeapSnapshot(v8_str("code"));
+ CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
const v8::HeapGraphNode* compiled =
- GetProperty(global, v8::HeapGraphEdge::kShortcut, "compiled");
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "compiled");
CHECK_NE(NULL, compiled);
CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType());
const v8::HeapGraphNode* lazy =
- GetProperty(global, v8::HeapGraphEdge::kShortcut, "lazy");
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy");
CHECK_NE(NULL, lazy);
CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
const v8::HeapGraphNode* anonymous =
- GetProperty(global, v8::HeapGraphEdge::kShortcut, "anonymous");
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "anonymous");
CHECK_NE(NULL, anonymous);
CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
- v8::String::AsciiValue anonymous_name(anonymous->GetName());
+ v8::String::Utf8Value anonymous_name(anonymous->GetName());
CHECK_EQ("", *anonymous_name);
// Find references to code.
@@ -255,6 +344,15 @@
GetProperty(lazy, v8::HeapGraphEdge::kInternal, "shared");
CHECK_NE(NULL, lazy_code);
+ // Check that there's no strong next_code_link. There might be a weak one
+ // but might be not, so we can't check that fact.
+ const v8::HeapGraphNode* code =
+ GetProperty(compiled_code, v8::HeapGraphEdge::kInternal, "code");
+ CHECK_NE(NULL, code);
+ const v8::HeapGraphNode* next_code_link =
+ GetProperty(code, v8::HeapGraphEdge::kInternal, "code");
+ CHECK_EQ(NULL, next_code_link);
+
// Verify that non-compiled code doesn't contain references to "x"
// literal, while compiled code does. The scope info is stored in FixedArray
// objects attached to the SharedFunctionInfo.
@@ -285,24 +383,28 @@
TEST(HeapSnapshotHeapNumbers) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
CompileRun(
"a = 1; // a is Smi\n"
"b = 2.5; // b is HeapNumber");
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("numbers"));
+ heap_profiler->TakeHeapSnapshot(v8_str("numbers"));
+ CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
- CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kShortcut, "a"));
+ CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kProperty, "a"));
const v8::HeapGraphNode* b =
- GetProperty(global, v8::HeapGraphEdge::kShortcut, "b");
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "b");
CHECK_NE(NULL, b);
CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType());
}
+
TEST(HeapSnapshotSlicedString) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
CompileRun(
"parent_string = \"123456789.123456789.123456789.123456789.123456789."
"123456789.123456789.123456789.123456789.123456789."
@@ -310,32 +412,251 @@
"123456789.123456789.123456789.123456789.123456789.\";"
"child_string = parent_string.slice(100);");
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("strings"));
+ heap_profiler->TakeHeapSnapshot(v8_str("strings"));
+ CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
const v8::HeapGraphNode* parent_string =
- GetProperty(global, v8::HeapGraphEdge::kShortcut, "parent_string");
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "parent_string");
CHECK_NE(NULL, parent_string);
const v8::HeapGraphNode* child_string =
- GetProperty(global, v8::HeapGraphEdge::kShortcut, "child_string");
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "child_string");
CHECK_NE(NULL, child_string);
+ CHECK_EQ(v8::HeapGraphNode::kSlicedString, child_string->GetType());
const v8::HeapGraphNode* parent =
GetProperty(child_string, v8::HeapGraphEdge::kInternal, "parent");
CHECK_EQ(parent_string, parent);
+ heap_profiler->DeleteAllHeapSnapshots();
}
+
+TEST(HeapSnapshotConsString) {
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope scope(isolate);
+ v8::Local<v8::ObjectTemplate> global_template =
+ v8::ObjectTemplate::New(isolate);
+ global_template->SetInternalFieldCount(1);
+ LocalContext env(NULL, global_template);
+ v8::Handle<v8::Object> global_proxy = env->Global();
+ v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
+ CHECK_EQ(1, global->InternalFieldCount());
+
+ i::Factory* factory = CcTest::i_isolate()->factory();
+ i::Handle<i::String> first = factory->NewStringFromStaticChars("0123456789");
+ i::Handle<i::String> second = factory->NewStringFromStaticChars("0123456789");
+ i::Handle<i::String> cons_string =
+ factory->NewConsString(first, second).ToHandleChecked();
+
+ global->SetInternalField(0, v8::ToApiHandle<v8::String>(cons_string));
+
+ v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("cons_strings"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
+
+ const v8::HeapGraphNode* string_node =
+ GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0");
+ CHECK_NE(NULL, string_node);
+ CHECK_EQ(v8::HeapGraphNode::kConsString, string_node->GetType());
+
+ const v8::HeapGraphNode* first_node =
+ GetProperty(string_node, v8::HeapGraphEdge::kInternal, "first");
+ CHECK_EQ(v8::HeapGraphNode::kString, first_node->GetType());
+
+ const v8::HeapGraphNode* second_node =
+ GetProperty(string_node, v8::HeapGraphEdge::kInternal, "second");
+ CHECK_EQ(v8::HeapGraphNode::kString, second_node->GetType());
+
+ heap_profiler->DeleteAllHeapSnapshots();
+}
+
+
+TEST(HeapSnapshotSymbol) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+
+ CompileRun("a = Symbol('mySymbol');\n");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("Symbol"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ const v8::HeapGraphNode* a =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
+ CHECK_NE(NULL, a);
+ CHECK_EQ(a->GetType(), v8::HeapGraphNode::kSymbol);
+ CHECK_EQ(v8_str("symbol"), a->GetName());
+ const v8::HeapGraphNode* name =
+ GetProperty(a, v8::HeapGraphEdge::kInternal, "name");
+ CHECK_NE(NULL, name);
+ CHECK_EQ(v8_str("mySymbol"), name->GetName());
+}
+
+
+TEST(HeapSnapshotWeakCollection) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+
+ CompileRun(
+ "k = {}; v = {}; s = 'str';\n"
+ "ws = new WeakSet(); ws.add(k); ws.add(v); ws[s] = s;\n"
+ "wm = new WeakMap(); wm.set(k, v); wm[s] = s;\n");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("WeakCollections"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ const v8::HeapGraphNode* k =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "k");
+ CHECK_NE(NULL, k);
+ const v8::HeapGraphNode* v =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "v");
+ CHECK_NE(NULL, v);
+ const v8::HeapGraphNode* s =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "s");
+ CHECK_NE(NULL, s);
+
+ const v8::HeapGraphNode* ws =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "ws");
+ CHECK_NE(NULL, ws);
+ CHECK_EQ(v8::HeapGraphNode::kObject, ws->GetType());
+ CHECK_EQ(v8_str("WeakSet"), ws->GetName());
+
+ const v8::HeapGraphNode* ws_table =
+ GetProperty(ws, v8::HeapGraphEdge::kInternal, "table");
+ CHECK_EQ(v8::HeapGraphNode::kArray, ws_table->GetType());
+ CHECK_GT(ws_table->GetChildrenCount(), 0);
+ int weak_entries = 0;
+ for (int i = 0, count = ws_table->GetChildrenCount(); i < count; ++i) {
+ const v8::HeapGraphEdge* prop = ws_table->GetChild(i);
+ if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue;
+ if (k->GetId() == prop->GetToNode()->GetId()) {
+ ++weak_entries;
+ }
+ }
+ CHECK_EQ(1, weak_entries);
+ const v8::HeapGraphNode* ws_s =
+ GetProperty(ws, v8::HeapGraphEdge::kProperty, "str");
+ CHECK_NE(NULL, ws_s);
+ CHECK_EQ(static_cast<int>(s->GetId()), static_cast<int>(ws_s->GetId()));
+
+ const v8::HeapGraphNode* wm =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "wm");
+ CHECK_NE(NULL, wm);
+ CHECK_EQ(v8::HeapGraphNode::kObject, wm->GetType());
+ CHECK_EQ(v8_str("WeakMap"), wm->GetName());
+
+ const v8::HeapGraphNode* wm_table =
+ GetProperty(wm, v8::HeapGraphEdge::kInternal, "table");
+ CHECK_EQ(v8::HeapGraphNode::kArray, wm_table->GetType());
+ CHECK_GT(wm_table->GetChildrenCount(), 0);
+ weak_entries = 0;
+ for (int i = 0, count = wm_table->GetChildrenCount(); i < count; ++i) {
+ const v8::HeapGraphEdge* prop = wm_table->GetChild(i);
+ if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue;
+ const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
+ if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
+ ++weak_entries;
+ }
+ }
+ CHECK_EQ(2, weak_entries);
+ const v8::HeapGraphNode* wm_s =
+ GetProperty(wm, v8::HeapGraphEdge::kProperty, "str");
+ CHECK_NE(NULL, wm_s);
+ CHECK_EQ(static_cast<int>(s->GetId()), static_cast<int>(wm_s->GetId()));
+}
+
+
+TEST(HeapSnapshotCollection) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+
+ CompileRun(
+ "k = {}; v = {}; s = 'str';\n"
+ "set = new Set(); set.add(k); set.add(v); set[s] = s;\n"
+ "map = new Map(); map.set(k, v); map[s] = s;\n");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("Collections"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ const v8::HeapGraphNode* k =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "k");
+ CHECK_NE(NULL, k);
+ const v8::HeapGraphNode* v =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "v");
+ CHECK_NE(NULL, v);
+ const v8::HeapGraphNode* s =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "s");
+ CHECK_NE(NULL, s);
+
+ const v8::HeapGraphNode* set =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "set");
+ CHECK_NE(NULL, set);
+ CHECK_EQ(v8::HeapGraphNode::kObject, set->GetType());
+ CHECK_EQ(v8_str("Set"), set->GetName());
+
+ const v8::HeapGraphNode* set_table =
+ GetProperty(set, v8::HeapGraphEdge::kInternal, "table");
+ CHECK_EQ(v8::HeapGraphNode::kArray, set_table->GetType());
+ CHECK_GT(set_table->GetChildrenCount(), 0);
+ int entries = 0;
+ for (int i = 0, count = set_table->GetChildrenCount(); i < count; ++i) {
+ const v8::HeapGraphEdge* prop = set_table->GetChild(i);
+ const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
+ if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
+ ++entries;
+ }
+ }
+ CHECK_EQ(2, entries);
+ const v8::HeapGraphNode* set_s =
+ GetProperty(set, v8::HeapGraphEdge::kProperty, "str");
+ CHECK_NE(NULL, set_s);
+ CHECK_EQ(static_cast<int>(s->GetId()), static_cast<int>(set_s->GetId()));
+
+ const v8::HeapGraphNode* map =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "map");
+ CHECK_NE(NULL, map);
+ CHECK_EQ(v8::HeapGraphNode::kObject, map->GetType());
+ CHECK_EQ(v8_str("Map"), map->GetName());
+
+ const v8::HeapGraphNode* map_table =
+ GetProperty(map, v8::HeapGraphEdge::kInternal, "table");
+ CHECK_EQ(v8::HeapGraphNode::kArray, map_table->GetType());
+ CHECK_GT(map_table->GetChildrenCount(), 0);
+ entries = 0;
+ for (int i = 0, count = map_table->GetChildrenCount(); i < count; ++i) {
+ const v8::HeapGraphEdge* prop = map_table->GetChild(i);
+ const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
+ if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
+ ++entries;
+ }
+ }
+ CHECK_EQ(2, entries);
+ const v8::HeapGraphNode* map_s =
+ GetProperty(map, v8::HeapGraphEdge::kProperty, "str");
+ CHECK_NE(NULL, map_s);
+ CHECK_EQ(static_cast<int>(s->GetId()), static_cast<int>(map_s->GetId()));
+}
+
+
TEST(HeapSnapshotInternalReferences) {
- v8::HandleScope scope;
- v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope scope(isolate);
+ v8::Local<v8::ObjectTemplate> global_template =
+ v8::ObjectTemplate::New(isolate);
global_template->SetInternalFieldCount(2);
LocalContext env(NULL, global_template);
v8::Handle<v8::Object> global_proxy = env->Global();
v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
CHECK_EQ(2, global->InternalFieldCount());
- v8::Local<v8::Object> obj = v8::Object::New();
+ v8::Local<v8::Object> obj = v8::Object::New(isolate);
global->SetInternalField(0, v8_num(17));
global->SetInternalField(1, obj);
+ v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("internals"));
+ heap_profiler->TakeHeapSnapshot(v8_str("internals"));
+ CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
// The first reference will not present, because it's a Smi.
CHECK_EQ(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0"));
@@ -344,17 +665,59 @@
}
-// Trying to introduce a check helper for uint64_t causes many
+// Trying to introduce a check helper for uint32_t causes many
// overloading ambiguities, so it seems easier just to cast
// them to a signed type.
-#define CHECK_EQ_UINT64_T(a, b) \
- CHECK_EQ(static_cast<int64_t>(a), static_cast<int64_t>(b))
-#define CHECK_NE_UINT64_T(a, b) \
+#define CHECK_EQ_SNAPSHOT_OBJECT_ID(a, b) \
+ CHECK_EQ(static_cast<int32_t>(a), static_cast<int32_t>(b))
+#define CHECK_NE_SNAPSHOT_OBJECT_ID(a, b) \
CHECK((a) != (b)) // NOLINT
-TEST(HeapEntryIdsAndArrayShift) {
- v8::HandleScope scope;
+TEST(HeapSnapshotAddressReuse) {
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+
+ CompileRun(
+ "function A() {}\n"
+ "var a = [];\n"
+ "for (var i = 0; i < 10000; ++i)\n"
+ " a[i] = new A();\n");
+ const v8::HeapSnapshot* snapshot1 =
+ heap_profiler->TakeHeapSnapshot(v8_str("snapshot1"));
+ CHECK(ValidateSnapshot(snapshot1));
+ v8::SnapshotObjectId maxId1 = snapshot1->GetMaxSnapshotJSObjectId();
+
+ CompileRun(
+ "for (var i = 0; i < 10000; ++i)\n"
+ " a[i] = new A();\n");
+ CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
+
+ const v8::HeapSnapshot* snapshot2 =
+ heap_profiler->TakeHeapSnapshot(v8_str("snapshot2"));
+ CHECK(ValidateSnapshot(snapshot2));
+ const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
+
+ const v8::HeapGraphNode* array_node =
+ GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
+ CHECK_NE(NULL, array_node);
+ int wrong_count = 0;
+ for (int i = 0, count = array_node->GetChildrenCount(); i < count; ++i) {
+ const v8::HeapGraphEdge* prop = array_node->GetChild(i);
+ if (prop->GetType() != v8::HeapGraphEdge::kElement)
+ continue;
+ v8::SnapshotObjectId id = prop->GetToNode()->GetId();
+ if (id < maxId1)
+ ++wrong_count;
+ }
+ CHECK_EQ(0, wrong_count);
+}
+
+
+TEST(HeapEntryIdsAndArrayShift) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
CompileRun(
"function AnObject() {\n"
@@ -365,107 +728,114 @@
"for (var i = 0; i < 10; ++i)\n"
" a.push(new AnObject());\n");
const v8::HeapSnapshot* snapshot1 =
- v8::HeapProfiler::TakeSnapshot(v8_str("s1"));
+ heap_profiler->TakeHeapSnapshot(v8_str("s1"));
+ CHECK(ValidateSnapshot(snapshot1));
CompileRun(
"for (var i = 0; i < 1; ++i)\n"
" a.shift();\n");
- HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
+ CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
const v8::HeapSnapshot* snapshot2 =
- v8::HeapProfiler::TakeSnapshot(v8_str("s2"));
+ heap_profiler->TakeHeapSnapshot(v8_str("s2"));
+ CHECK(ValidateSnapshot(snapshot2));
const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
- CHECK_NE_UINT64_T(0, global1->GetId());
- CHECK_EQ_UINT64_T(global1->GetId(), global2->GetId());
+ CHECK_NE_SNAPSHOT_OBJECT_ID(0, global1->GetId());
+ CHECK_EQ_SNAPSHOT_OBJECT_ID(global1->GetId(), global2->GetId());
const v8::HeapGraphNode* a1 =
GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
CHECK_NE(NULL, a1);
- const v8::HeapGraphNode* e1 =
- GetProperty(a1, v8::HeapGraphEdge::kHidden, "1");
- CHECK_NE(NULL, e1);
const v8::HeapGraphNode* k1 =
- GetProperty(e1, v8::HeapGraphEdge::kInternal, "elements");
+ GetProperty(a1, v8::HeapGraphEdge::kInternal, "elements");
CHECK_NE(NULL, k1);
const v8::HeapGraphNode* a2 =
GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
CHECK_NE(NULL, a2);
- const v8::HeapGraphNode* e2 =
- GetProperty(a2, v8::HeapGraphEdge::kHidden, "1");
- CHECK_NE(NULL, e2);
const v8::HeapGraphNode* k2 =
- GetProperty(e2, v8::HeapGraphEdge::kInternal, "elements");
+ GetProperty(a2, v8::HeapGraphEdge::kInternal, "elements");
CHECK_NE(NULL, k2);
- CHECK_EQ_UINT64_T(a1->GetId(), a2->GetId());
- CHECK_EQ_UINT64_T(e1->GetId(), e2->GetId());
- CHECK_EQ_UINT64_T(k1->GetId(), k2->GetId());
+ CHECK_EQ_SNAPSHOT_OBJECT_ID(a1->GetId(), a2->GetId());
+ CHECK_EQ_SNAPSHOT_OBJECT_ID(k1->GetId(), k2->GetId());
}
+
TEST(HeapEntryIdsAndGC) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
CompileRun(
"function A() {}\n"
"function B(x) { this.x = x; }\n"
"var a = new A();\n"
"var b = new B(a);");
+ v8::Local<v8::String> s1_str = v8_str("s1");
+ v8::Local<v8::String> s2_str = v8_str("s2");
const v8::HeapSnapshot* snapshot1 =
- v8::HeapProfiler::TakeSnapshot(v8_str("s1"));
+ heap_profiler->TakeHeapSnapshot(s1_str);
+ CHECK(ValidateSnapshot(snapshot1));
- HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
+ CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
const v8::HeapSnapshot* snapshot2 =
- v8::HeapProfiler::TakeSnapshot(v8_str("s2"));
+ heap_profiler->TakeHeapSnapshot(s2_str);
+ CHECK(ValidateSnapshot(snapshot2));
+
+ CHECK_GT(snapshot1->GetMaxSnapshotJSObjectId(), 7000);
+ CHECK(snapshot1->GetMaxSnapshotJSObjectId() <=
+ snapshot2->GetMaxSnapshotJSObjectId());
const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
- CHECK_NE_UINT64_T(0, global1->GetId());
- CHECK_EQ_UINT64_T(global1->GetId(), global2->GetId());
+ CHECK_NE_SNAPSHOT_OBJECT_ID(0, global1->GetId());
+ CHECK_EQ_SNAPSHOT_OBJECT_ID(global1->GetId(), global2->GetId());
const v8::HeapGraphNode* A1 =
GetProperty(global1, v8::HeapGraphEdge::kProperty, "A");
CHECK_NE(NULL, A1);
const v8::HeapGraphNode* A2 =
GetProperty(global2, v8::HeapGraphEdge::kProperty, "A");
CHECK_NE(NULL, A2);
- CHECK_NE_UINT64_T(0, A1->GetId());
- CHECK_EQ_UINT64_T(A1->GetId(), A2->GetId());
+ CHECK_NE_SNAPSHOT_OBJECT_ID(0, A1->GetId());
+ CHECK_EQ_SNAPSHOT_OBJECT_ID(A1->GetId(), A2->GetId());
const v8::HeapGraphNode* B1 =
GetProperty(global1, v8::HeapGraphEdge::kProperty, "B");
CHECK_NE(NULL, B1);
const v8::HeapGraphNode* B2 =
GetProperty(global2, v8::HeapGraphEdge::kProperty, "B");
CHECK_NE(NULL, B2);
- CHECK_NE_UINT64_T(0, B1->GetId());
- CHECK_EQ_UINT64_T(B1->GetId(), B2->GetId());
+ CHECK_NE_SNAPSHOT_OBJECT_ID(0, B1->GetId());
+ CHECK_EQ_SNAPSHOT_OBJECT_ID(B1->GetId(), B2->GetId());
const v8::HeapGraphNode* a1 =
GetProperty(global1, v8::HeapGraphEdge::kProperty, "a");
CHECK_NE(NULL, a1);
const v8::HeapGraphNode* a2 =
GetProperty(global2, v8::HeapGraphEdge::kProperty, "a");
CHECK_NE(NULL, a2);
- CHECK_NE_UINT64_T(0, a1->GetId());
- CHECK_EQ_UINT64_T(a1->GetId(), a2->GetId());
+ CHECK_NE_SNAPSHOT_OBJECT_ID(0, a1->GetId());
+ CHECK_EQ_SNAPSHOT_OBJECT_ID(a1->GetId(), a2->GetId());
const v8::HeapGraphNode* b1 =
GetProperty(global1, v8::HeapGraphEdge::kProperty, "b");
CHECK_NE(NULL, b1);
const v8::HeapGraphNode* b2 =
GetProperty(global2, v8::HeapGraphEdge::kProperty, "b");
CHECK_NE(NULL, b2);
- CHECK_NE_UINT64_T(0, b1->GetId());
- CHECK_EQ_UINT64_T(b1->GetId(), b2->GetId());
+ CHECK_NE_SNAPSHOT_OBJECT_ID(0, b1->GetId());
+ CHECK_EQ_SNAPSHOT_OBJECT_ID(b1->GetId(), b2->GetId());
}
TEST(HeapSnapshotRootPreservedAfterSorting) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("s"));
+ heap_profiler->TakeHeapSnapshot(v8_str("s"));
+ CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* root1 = snapshot->GetRoot();
const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>(
snapshot))->GetSortedEntriesList();
@@ -474,66 +844,6 @@
}
-TEST(HeapEntryDominator) {
- // The graph looks like this:
- //
- // -> node1
- // a |^
- // -> node5 ba
- // a v|
- // node6 -> node2
- // b a |^
- // -> node4 ba
- // b v|
- // -> node3
- //
- // The dominator for all nodes is node6.
-
- v8::HandleScope scope;
- LocalContext env;
-
- CompileRun(
- "function X(a, b) { this.a = a; this.b = b; }\n"
- "node6 = new X(new X(new X()), new X(new X(),new X()));\n"
- "(function(){\n"
- "node6.a.a.b = node6.b.a; // node1 -> node2\n"
- "node6.b.a.a = node6.a.a; // node2 -> node1\n"
- "node6.b.a.b = node6.b.b; // node2 -> node3\n"
- "node6.b.b.a = node6.b.a; // node3 -> node2\n"
- "})();");
-
- const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("dominators"));
-
- const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
- CHECK_NE(NULL, global);
- const v8::HeapGraphNode* node6 =
- GetProperty(global, v8::HeapGraphEdge::kShortcut, "node6");
- CHECK_NE(NULL, node6);
- const v8::HeapGraphNode* node5 =
- GetProperty(node6, v8::HeapGraphEdge::kProperty, "a");
- CHECK_NE(NULL, node5);
- const v8::HeapGraphNode* node4 =
- GetProperty(node6, v8::HeapGraphEdge::kProperty, "b");
- CHECK_NE(NULL, node4);
- const v8::HeapGraphNode* node3 =
- GetProperty(node4, v8::HeapGraphEdge::kProperty, "b");
- CHECK_NE(NULL, node3);
- const v8::HeapGraphNode* node2 =
- GetProperty(node4, v8::HeapGraphEdge::kProperty, "a");
- CHECK_NE(NULL, node2);
- const v8::HeapGraphNode* node1 =
- GetProperty(node5, v8::HeapGraphEdge::kProperty, "a");
- CHECK_NE(NULL, node1);
-
- CHECK_EQ(node6, node1->GetDominatorNode());
- CHECK_EQ(node6, node2->GetDominatorNode());
- CHECK_EQ(node6, node3->GetDominatorNode());
- CHECK_EQ(node6, node4->GetDominatorNode());
- CHECK_EQ(node6, node5->GetDominatorNode());
-}
-
-
namespace {
class TestJSONStream : public v8::OutputStream {
@@ -548,21 +858,26 @@
if (abort_countdown_ == 0) return kAbort;
CHECK_GT(chars_written, 0);
i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
- memcpy(chunk.start(), buffer, chars_written);
+ i::MemCopy(chunk.start(), buffer, chars_written);
return kContinue;
}
+ virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int chars_written) {
+ DCHECK(false);
+ return kAbort;
+ }
void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
int eos_signaled() { return eos_signaled_; }
int size() { return buffer_.size(); }
+
private:
i::Collector<char> buffer_;
int eos_signaled_;
int abort_countdown_;
};
-class AsciiResource: public v8::String::ExternalAsciiStringResource {
+class OneByteResource : public v8::String::ExternalOneByteStringResource {
public:
- explicit AsciiResource(i::Vector<char> string): data_(string.start()) {
+ explicit OneByteResource(i::Vector<char> string) : data_(string.start()) {
length_ = string.length();
}
virtual const char* data() const { return data_; }
@@ -575,8 +890,9 @@
} // namespace
TEST(HeapSnapshotJSONSerialization) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
#define STRING_LITERAL_FOR_TEST \
"\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
@@ -586,7 +902,9 @@
"var a = new A(" STRING_LITERAL_FOR_TEST ");\n"
"var b = new B(a);");
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("json"));
+ heap_profiler->TakeHeapSnapshot(v8_str("json"));
+ CHECK(ValidateSnapshot(snapshot));
+
TestJSONStream stream;
snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
CHECK_GT(stream.size(), 0);
@@ -595,8 +913,9 @@
stream.WriteTo(json);
// Verify that snapshot string is valid JSON.
- AsciiResource json_res(json);
- v8::Local<v8::String> json_string = v8::String::NewExternal(&json_res);
+ OneByteResource* json_res = new OneByteResource(json);
+ v8::Local<v8::String> json_string =
+ v8::String::NewExternal(env->GetIsolate(), json_res);
env->Global()->Set(v8_str("json_snapshot"), json_string);
v8::Local<v8::Value> snapshot_parse_result = CompileRun(
"var parsed = JSON.parse(json_snapshot); true;");
@@ -607,42 +926,45 @@
env->Global()->Get(v8_str("parsed"))->ToObject();
CHECK(parsed_snapshot->Has(v8_str("snapshot")));
CHECK(parsed_snapshot->Has(v8_str("nodes")));
+ CHECK(parsed_snapshot->Has(v8_str("edges")));
CHECK(parsed_snapshot->Has(v8_str("strings")));
// Get node and edge "member" offsets.
v8::Local<v8::Value> meta_analysis_result = CompileRun(
- "var parsed_meta = parsed.nodes[0];\n"
- "var children_count_offset ="
- " parsed_meta.fields.indexOf('children_count');\n"
- "var children_offset ="
- " parsed_meta.fields.indexOf('children');\n"
- "var children_meta ="
- " parsed_meta.types[children_offset];\n"
- "var child_fields_count = children_meta.fields.length;\n"
- "var child_type_offset ="
- " children_meta.fields.indexOf('type');\n"
- "var child_name_offset ="
- " children_meta.fields.indexOf('name_or_index');\n"
- "var child_to_node_offset ="
- " children_meta.fields.indexOf('to_node');\n"
+ "var meta = parsed.snapshot.meta;\n"
+ "var edge_count_offset = meta.node_fields.indexOf('edge_count');\n"
+ "var node_fields_count = meta.node_fields.length;\n"
+ "var edge_fields_count = meta.edge_fields.length;\n"
+ "var edge_type_offset = meta.edge_fields.indexOf('type');\n"
+ "var edge_name_offset = meta.edge_fields.indexOf('name_or_index');\n"
+ "var edge_to_node_offset = meta.edge_fields.indexOf('to_node');\n"
"var property_type ="
- " children_meta.types[child_type_offset].indexOf('property');\n"
+ " meta.edge_types[edge_type_offset].indexOf('property');\n"
"var shortcut_type ="
- " children_meta.types[child_type_offset].indexOf('shortcut');");
+ " meta.edge_types[edge_type_offset].indexOf('shortcut');\n"
+ "var node_count = parsed.nodes.length / node_fields_count;\n"
+ "var first_edge_indexes = parsed.first_edge_indexes = [];\n"
+ "for (var i = 0, first_edge_index = 0; i < node_count; ++i) {\n"
+ " first_edge_indexes[i] = first_edge_index;\n"
+ " first_edge_index += edge_fields_count *\n"
+ " parsed.nodes[i * node_fields_count + edge_count_offset];\n"
+ "}\n"
+ "first_edge_indexes[node_count] = first_edge_index;\n");
CHECK(!meta_analysis_result.IsEmpty());
// A helper function for processing encoded nodes.
CompileRun(
"function GetChildPosByProperty(pos, prop_name, prop_type) {\n"
" var nodes = parsed.nodes;\n"
+ " var edges = parsed.edges;\n"
" var strings = parsed.strings;\n"
- " for (var i = 0,\n"
- " count = nodes[pos + children_count_offset] * child_fields_count;\n"
- " i < count; i += child_fields_count) {\n"
- " var child_pos = pos + children_offset + i;\n"
- " if (nodes[child_pos + child_type_offset] === prop_type\n"
- " && strings[nodes[child_pos + child_name_offset]] === prop_name)\n"
- " return nodes[child_pos + child_to_node_offset];\n"
+ " var node_ordinal = pos / node_fields_count;\n"
+ " for (var i = parsed.first_edge_indexes[node_ordinal],\n"
+ " count = parsed.first_edge_indexes[node_ordinal + 1];\n"
+ " i < count; i += edge_fields_count) {\n"
+ " if (edges[i + edge_type_offset] === prop_type\n"
+ " && strings[edges[i + edge_name_offset]] === prop_name)\n"
+ " return edges[i + edge_to_node_offset];\n"
" }\n"
" return null;\n"
"}\n");
@@ -651,8 +973,8 @@
"GetChildPosByProperty(\n"
" GetChildPosByProperty(\n"
" GetChildPosByProperty("
- " parsed.nodes[1 + children_offset + child_to_node_offset],"
- " \"b\",shortcut_type),\n"
+ " parsed.edges[edge_fields_count + edge_to_node_offset],"
+ " \"b\", property_type),\n"
" \"x\", property_type),"
" \"s\", property_type)");
CHECK(!string_obj_pos_val.IsEmpty());
@@ -675,16 +997,270 @@
TEST(HeapSnapshotJSONSerializationAborting) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("abort"));
+ heap_profiler->TakeHeapSnapshot(v8_str("abort"));
+ CHECK(ValidateSnapshot(snapshot));
TestJSONStream stream(5);
snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
CHECK_GT(stream.size(), 0);
CHECK_EQ(0, stream.eos_signaled());
}
+namespace {
+
+class TestStatsStream : public v8::OutputStream {
+ public:
+ TestStatsStream()
+ : eos_signaled_(0),
+ updates_written_(0),
+ entries_count_(0),
+ entries_size_(0),
+ intervals_count_(0),
+ first_interval_index_(-1) { }
+ TestStatsStream(const TestStatsStream& stream)
+ : v8::OutputStream(stream),
+ eos_signaled_(stream.eos_signaled_),
+ updates_written_(stream.updates_written_),
+ entries_count_(stream.entries_count_),
+ entries_size_(stream.entries_size_),
+ intervals_count_(stream.intervals_count_),
+ first_interval_index_(stream.first_interval_index_) { }
+ virtual ~TestStatsStream() {}
+ virtual void EndOfStream() { ++eos_signaled_; }
+ virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
+ DCHECK(false);
+ return kAbort;
+ }
+ virtual WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* buffer,
+ int updates_written) {
+ ++intervals_count_;
+ DCHECK(updates_written);
+ updates_written_ += updates_written;
+ entries_count_ = 0;
+ if (first_interval_index_ == -1 && updates_written != 0)
+ first_interval_index_ = buffer[0].index;
+ for (int i = 0; i < updates_written; ++i) {
+ entries_count_ += buffer[i].count;
+ entries_size_ += buffer[i].size;
+ }
+
+ return kContinue;
+ }
+ int eos_signaled() { return eos_signaled_; }
+ int updates_written() { return updates_written_; }
+ uint32_t entries_count() const { return entries_count_; }
+ uint32_t entries_size() const { return entries_size_; }
+ int intervals_count() const { return intervals_count_; }
+ int first_interval_index() const { return first_interval_index_; }
+
+ private:
+ int eos_signaled_;
+ int updates_written_;
+ uint32_t entries_count_;
+ uint32_t entries_size_;
+ int intervals_count_;
+ int first_interval_index_;
+};
+
+} // namespace
+
+static TestStatsStream GetHeapStatsUpdate(
+ v8::HeapProfiler* heap_profiler,
+ v8::SnapshotObjectId* object_id = NULL) {
+ TestStatsStream stream;
+ v8::SnapshotObjectId last_seen_id = heap_profiler->GetHeapStats(&stream);
+ if (object_id)
+ *object_id = last_seen_id;
+ CHECK_EQ(1, stream.eos_signaled());
+ return stream;
+}
+
+
+TEST(HeapSnapshotObjectsStats) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+
+ heap_profiler->StartTrackingHeapObjects();
+ // We have to call GC 6 times. In other case the garbage will be
+ // the reason of flakiness.
+ for (int i = 0; i < 6; ++i) {
+ CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
+ }
+
+ v8::SnapshotObjectId initial_id;
+ {
+ // Single chunk of data expected in update. Initial data.
+ TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler,
+ &initial_id);
+ CHECK_EQ(1, stats_update.intervals_count());
+ CHECK_EQ(1, stats_update.updates_written());
+ CHECK_LT(0, stats_update.entries_size());
+ CHECK_EQ(0, stats_update.first_interval_index());
+ }
+
+ // No data expected in update because nothing has happened.
+ v8::SnapshotObjectId same_id;
+ CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &same_id).updates_written());
+ CHECK_EQ_SNAPSHOT_OBJECT_ID(initial_id, same_id);
+
+ {
+ v8::SnapshotObjectId additional_string_id;
+ v8::HandleScope inner_scope_1(env->GetIsolate());
+ v8_str("string1");
+ {
+ // Single chunk of data with one new entry expected in update.
+ TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler,
+ &additional_string_id);
+ CHECK_LT(same_id, additional_string_id);
+ CHECK_EQ(1, stats_update.intervals_count());
+ CHECK_EQ(1, stats_update.updates_written());
+ CHECK_LT(0, stats_update.entries_size());
+ CHECK_EQ(1, stats_update.entries_count());
+ CHECK_EQ(2, stats_update.first_interval_index());
+ }
+
+ // No data expected in update because nothing happened.
+ v8::SnapshotObjectId last_id;
+ CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &last_id).updates_written());
+ CHECK_EQ_SNAPSHOT_OBJECT_ID(additional_string_id, last_id);
+
+ {
+ v8::HandleScope inner_scope_2(env->GetIsolate());
+ v8_str("string2");
+
+ uint32_t entries_size;
+ {
+ v8::HandleScope inner_scope_3(env->GetIsolate());
+ v8_str("string3");
+ v8_str("string4");
+
+ {
+ // Single chunk of data with three new entries expected in update.
+ TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
+ CHECK_EQ(1, stats_update.intervals_count());
+ CHECK_EQ(1, stats_update.updates_written());
+ CHECK_LT(0, entries_size = stats_update.entries_size());
+ CHECK_EQ(3, stats_update.entries_count());
+ CHECK_EQ(4, stats_update.first_interval_index());
+ }
+ }
+
+ {
+ // Single chunk of data with two left entries expected in update.
+ TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
+ CHECK_EQ(1, stats_update.intervals_count());
+ CHECK_EQ(1, stats_update.updates_written());
+ CHECK_GT(entries_size, stats_update.entries_size());
+ CHECK_EQ(1, stats_update.entries_count());
+ // Two strings from forth interval were released.
+ CHECK_EQ(4, stats_update.first_interval_index());
+ }
+ }
+
+ {
+ // Single chunk of data with 0 left entries expected in update.
+ TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
+ CHECK_EQ(1, stats_update.intervals_count());
+ CHECK_EQ(1, stats_update.updates_written());
+ CHECK_EQ(0, stats_update.entries_size());
+ CHECK_EQ(0, stats_update.entries_count());
+ // The last string from forth interval was released.
+ CHECK_EQ(4, stats_update.first_interval_index());
+ }
+ }
+ {
+ // Single chunk of data with 0 left entries expected in update.
+ TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
+ CHECK_EQ(1, stats_update.intervals_count());
+ CHECK_EQ(1, stats_update.updates_written());
+ CHECK_EQ(0, stats_update.entries_size());
+ CHECK_EQ(0, stats_update.entries_count());
+ // The only string from the second interval was released.
+ CHECK_EQ(2, stats_update.first_interval_index());
+ }
+
+ v8::Local<v8::Array> array = v8::Array::New(env->GetIsolate());
+ CHECK_EQ(0, array->Length());
+ // Force array's buffer allocation.
+ array->Set(2, v8_num(7));
+
+ uint32_t entries_size;
+ {
+ // Single chunk of data with 2 entries expected in update.
+ TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
+ CHECK_EQ(1, stats_update.intervals_count());
+ CHECK_EQ(1, stats_update.updates_written());
+ CHECK_LT(0, entries_size = stats_update.entries_size());
+ // They are the array and its buffer.
+ CHECK_EQ(2, stats_update.entries_count());
+ CHECK_EQ(8, stats_update.first_interval_index());
+ }
+
+ for (int i = 0; i < 100; ++i)
+ array->Set(i, v8_num(i));
+
+ {
+ // Single chunk of data with 1 entry expected in update.
+ TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
+ CHECK_EQ(1, stats_update.intervals_count());
+ // The first interval was changed because old buffer was collected.
+ // The second interval was changed because new buffer was allocated.
+ CHECK_EQ(2, stats_update.updates_written());
+ CHECK_LT(entries_size, stats_update.entries_size());
+ CHECK_EQ(2, stats_update.entries_count());
+ CHECK_EQ(8, stats_update.first_interval_index());
+ }
+
+ heap_profiler->StopTrackingHeapObjects();
+}
+
+
+TEST(HeapObjectIds) {
+ LocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+
+ const int kLength = 10;
+ v8::Handle<v8::Object> objects[kLength];
+ v8::SnapshotObjectId ids[kLength];
+
+ heap_profiler->StartTrackingHeapObjects(false);
+
+ for (int i = 0; i < kLength; i++) {
+ objects[i] = v8::Object::New(isolate);
+ }
+ GetHeapStatsUpdate(heap_profiler);
+
+ for (int i = 0; i < kLength; i++) {
+ v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
+ CHECK_NE(v8::HeapProfiler::kUnknownObjectId, static_cast<int>(id));
+ ids[i] = id;
+ }
+
+ heap_profiler->StopTrackingHeapObjects();
+ CcTest::heap()->CollectAllAvailableGarbage();
+
+ for (int i = 0; i < kLength; i++) {
+ v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
+ CHECK_EQ(static_cast<int>(ids[i]), static_cast<int>(id));
+ v8::Handle<v8::Value> obj = heap_profiler->FindObjectById(ids[i]);
+ CHECK_EQ(objects[i], obj);
+ }
+
+ heap_profiler->ClearObjectIds();
+ for (int i = 0; i < kLength; i++) {
+ v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
+ CHECK_EQ(v8::HeapProfiler::kUnknownObjectId, static_cast<int>(id));
+ v8::Handle<v8::Value> obj = heap_profiler->FindObjectById(ids[i]);
+ CHECK(obj.IsEmpty());
+ }
+}
+
static void CheckChildrenIds(const v8::HeapSnapshot* snapshot,
const v8::HeapGraphNode* node,
@@ -695,7 +1271,7 @@
const v8::HeapGraphEdge* prop = node->GetChild(i);
const v8::HeapGraphNode* child =
snapshot->GetNodeById(prop->GetToNode()->GetId());
- CHECK_EQ_UINT64_T(prop->GetToNode()->GetId(), child->GetId());
+ CHECK_EQ_SNAPSHOT_OBJECT_ID(prop->GetToNode()->GetId(), child->GetId());
CHECK_EQ(prop->GetToNode(), child);
CheckChildrenIds(snapshot, child, level + 1, max_level);
}
@@ -703,11 +1279,13 @@
TEST(HeapSnapshotGetNodeById) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("id"));
+ heap_profiler->TakeHeapSnapshot(v8_str("id"));
+ CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* root = snapshot->GetRoot();
CheckChildrenIds(snapshot, root, 0, 3);
// Check a big id, which should not exist yet.
@@ -715,6 +1293,45 @@
}
+TEST(HeapSnapshotGetSnapshotObjectId) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ CompileRun("globalObject = {};\n");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("get_snapshot_object_id"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ const v8::HeapGraphNode* global_object =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "globalObject");
+ CHECK(global_object);
+
+ v8::Local<v8::Value> globalObjectHandle = env->Global()->Get(
+ v8::String::NewFromUtf8(env->GetIsolate(), "globalObject"));
+ CHECK(!globalObjectHandle.IsEmpty());
+ CHECK(globalObjectHandle->IsObject());
+
+ v8::SnapshotObjectId id = heap_profiler->GetObjectId(globalObjectHandle);
+ CHECK_NE(static_cast<int>(v8::HeapProfiler::kUnknownObjectId),
+ id);
+ CHECK_EQ(static_cast<int>(id), global_object->GetId());
+}
+
+
+TEST(HeapSnapshotUnknownSnapshotObjectId) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ CompileRun("globalObject = {};\n");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("unknown_object_id"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* node =
+ snapshot->GetNodeById(v8::HeapProfiler::kUnknownObjectId);
+ CHECK_EQ(NULL, node);
+}
+
+
namespace {
class TestActivityControl : public v8::ActivityControl {
@@ -736,27 +1353,29 @@
};
}
-TEST(TakeHeapSnapshotAborting) {
- v8::HandleScope scope;
- LocalContext env;
- const int snapshots_count = v8::HeapProfiler::GetSnapshotsCount();
+TEST(TakeHeapSnapshotAborting) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ const int snapshots_count = heap_profiler->GetSnapshotCount();
TestActivityControl aborting_control(1);
const v8::HeapSnapshot* no_snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("abort"),
- v8::HeapSnapshot::kFull,
+ heap_profiler->TakeHeapSnapshot(v8_str("abort"),
&aborting_control);
CHECK_EQ(NULL, no_snapshot);
- CHECK_EQ(snapshots_count, v8::HeapProfiler::GetSnapshotsCount());
+ CHECK_EQ(snapshots_count, heap_profiler->GetSnapshotCount());
CHECK_GT(aborting_control.total(), aborting_control.done());
TestActivityControl control(-1); // Don't abort.
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("full"),
- v8::HeapSnapshot::kFull,
+ heap_profiler->TakeHeapSnapshot(v8_str("full"),
&control);
+ CHECK(ValidateSnapshot(snapshot));
+
CHECK_NE(NULL, snapshot);
- CHECK_EQ(snapshots_count + 1, v8::HeapProfiler::GetSnapshotsCount());
+ CHECK_EQ(snapshots_count + 1, heap_profiler->GetSnapshotCount());
CHECK_EQ(control.total(), control.done());
CHECK_GT(control.total(), 0);
}
@@ -798,16 +1417,16 @@
uint16_t class_id, v8::Handle<v8::Value> wrapper) {
if (class_id == 1) {
if (wrapper->IsString()) {
- v8::String::AsciiValue ascii(wrapper);
- if (strcmp(*ascii, "AAA") == 0)
+ v8::String::Utf8Value utf8(wrapper);
+ if (strcmp(*utf8, "AAA") == 0)
return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
- else if (strcmp(*ascii, "BBB") == 0)
+ else if (strcmp(*utf8, "BBB") == 0)
return new TestRetainedObjectInfo(1, "aaa-group", "aaa", 100);
}
} else if (class_id == 2) {
if (wrapper->IsString()) {
- v8::String::AsciiValue ascii(wrapper);
- if (strcmp(*ascii, "CCC") == 0)
+ v8::String::Utf8Value utf8(wrapper);
+ if (strcmp(*utf8, "CCC") == 0)
return new TestRetainedObjectInfo(2, "ccc-group", "ccc");
}
}
@@ -819,7 +1438,6 @@
private:
bool disposed_;
- int category_;
int hash_;
const char* group_label_;
const char* label_;
@@ -848,25 +1466,25 @@
TEST(HeapSnapshotRetainedObjectInfo) {
- v8::HandleScope scope;
LocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
+ v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
- v8::HeapProfiler::DefineWrapperClass(
+ heap_profiler->SetWrapperClassInfoProvider(
1, TestRetainedObjectInfo::WrapperInfoCallback);
- v8::HeapProfiler::DefineWrapperClass(
+ heap_profiler->SetWrapperClassInfoProvider(
2, TestRetainedObjectInfo::WrapperInfoCallback);
- v8::Persistent<v8::String> p_AAA =
- v8::Persistent<v8::String>::New(v8_str("AAA"));
+ v8::Persistent<v8::String> p_AAA(isolate, v8_str("AAA"));
p_AAA.SetWrapperClassId(1);
- v8::Persistent<v8::String> p_BBB =
- v8::Persistent<v8::String>::New(v8_str("BBB"));
+ v8::Persistent<v8::String> p_BBB(isolate, v8_str("BBB"));
p_BBB.SetWrapperClassId(1);
- v8::Persistent<v8::String> p_CCC =
- v8::Persistent<v8::String>::New(v8_str("CCC"));
+ v8::Persistent<v8::String> p_CCC(isolate, v8_str("CCC"));
p_CCC.SetWrapperClassId(2);
CHECK_EQ(0, TestRetainedObjectInfo::instances.length());
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("retained"));
+ heap_profiler->TakeHeapSnapshot(v8_str("retained"));
+ CHECK(ValidateSnapshot(snapshot));
CHECK_EQ(3, TestRetainedObjectInfo::instances.length());
for (int i = 0; i < TestRetainedObjectInfo::instances.length(); ++i) {
@@ -912,50 +1530,58 @@
explicit GraphWithImplicitRefs(LocalContext* env) {
CHECK_EQ(NULL, instance_);
instance_ = this;
+ isolate_ = (*env)->GetIsolate();
for (int i = 0; i < kObjectsCount; i++) {
- objects_[i] = v8::Persistent<v8::Object>::New(v8::Object::New());
+ objects_[i].Reset(isolate_, v8::Object::New(isolate_));
}
- (*env)->Global()->Set(v8_str("root_object"), objects_[0]);
+ (*env)->Global()->Set(v8_str("root_object"),
+ v8::Local<v8::Value>::New(isolate_, objects_[0]));
}
~GraphWithImplicitRefs() {
instance_ = NULL;
}
- static void gcPrologue() {
+ static void gcPrologue(v8::GCType type, v8::GCCallbackFlags flags) {
instance_->AddImplicitReferences();
}
private:
void AddImplicitReferences() {
// 0 -> 1
- v8::V8::AddImplicitReferences(
- v8::Persistent<v8::Object>::Cast(objects_[0]), &objects_[1], 1);
- // Adding two more references(note length=2 in params): 1 -> 2, 1 -> 3
- v8::V8::AddImplicitReferences(
- v8::Persistent<v8::Object>::Cast(objects_[1]), &objects_[2], 2);
+ isolate_->SetObjectGroupId(objects_[0],
+ v8::UniqueId(1));
+ isolate_->SetReferenceFromGroup(
+ v8::UniqueId(1), objects_[1]);
+ // Adding two more references: 1 -> 2, 1 -> 3
+ isolate_->SetReference(objects_[1].As<v8::Object>(),
+ objects_[2]);
+ isolate_->SetReference(objects_[1].As<v8::Object>(),
+ objects_[3]);
}
v8::Persistent<v8::Value> objects_[kObjectsCount];
static GraphWithImplicitRefs* instance_;
+ v8::Isolate* isolate_;
};
GraphWithImplicitRefs* GraphWithImplicitRefs::instance_ = NULL;
TEST(HeapSnapshotImplicitReferences) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
GraphWithImplicitRefs graph(&env);
- v8::V8::SetGlobalGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue);
+ v8::V8::AddGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue);
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("implicit_refs"));
+ heap_profiler->TakeHeapSnapshot(v8_str("implicit_refs"));
+ CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global_object = GetGlobalObject(snapshot);
- // Use kShortcut type to skip intermediate JSGlobalPropertyCell
const v8::HeapGraphNode* obj0 = GetProperty(
- global_object, v8::HeapGraphEdge::kShortcut, "root_object");
+ global_object, v8::HeapGraphEdge::kProperty, "root_object");
CHECK(obj0);
CHECK_EQ(v8::HeapGraphNode::kObject, obj0->GetType());
const v8::HeapGraphNode* obj1 = GetProperty(
@@ -964,144 +1590,167 @@
int implicit_targets_count = 0;
for (int i = 0, count = obj1->GetChildrenCount(); i < count; ++i) {
const v8::HeapGraphEdge* prop = obj1->GetChild(i);
- v8::String::AsciiValue prop_name(prop->GetName());
+ v8::String::Utf8Value prop_name(prop->GetName());
if (prop->GetType() == v8::HeapGraphEdge::kInternal &&
strcmp("native", *prop_name) == 0) {
++implicit_targets_count;
}
}
CHECK_EQ(2, implicit_targets_count);
- v8::V8::SetGlobalGCPrologueCallback(NULL);
+ v8::V8::RemoveGCPrologueCallback(&GraphWithImplicitRefs::gcPrologue);
}
TEST(DeleteAllHeapSnapshots) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
- CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
- v8::HeapProfiler::DeleteAllSnapshots();
- CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
- CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8_str("1")));
- CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
- v8::HeapProfiler::DeleteAllSnapshots();
- CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
- CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8_str("1")));
- CHECK_NE(NULL, v8::HeapProfiler::TakeSnapshot(v8_str("2")));
- CHECK_EQ(2, v8::HeapProfiler::GetSnapshotsCount());
- v8::HeapProfiler::DeleteAllSnapshots();
- CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
+ CHECK_EQ(0, heap_profiler->GetSnapshotCount());
+ heap_profiler->DeleteAllHeapSnapshots();
+ CHECK_EQ(0, heap_profiler->GetSnapshotCount());
+ CHECK_NE(NULL, heap_profiler->TakeHeapSnapshot(v8_str("1")));
+ CHECK_EQ(1, heap_profiler->GetSnapshotCount());
+ heap_profiler->DeleteAllHeapSnapshots();
+ CHECK_EQ(0, heap_profiler->GetSnapshotCount());
+ CHECK_NE(NULL, heap_profiler->TakeHeapSnapshot(v8_str("1")));
+ CHECK_NE(NULL, heap_profiler->TakeHeapSnapshot(v8_str("2")));
+ CHECK_EQ(2, heap_profiler->GetSnapshotCount());
+ heap_profiler->DeleteAllHeapSnapshots();
+ CHECK_EQ(0, heap_profiler->GetSnapshotCount());
+}
+
+
+static const v8::HeapSnapshot* FindHeapSnapshot(v8::HeapProfiler* profiler,
+ unsigned uid) {
+ int length = profiler->GetSnapshotCount();
+ for (int i = 0; i < length; i++) {
+ const v8::HeapSnapshot* snapshot = profiler->GetHeapSnapshot(i);
+ if (snapshot->GetUid() == uid) {
+ return snapshot;
+ }
+ }
+ return NULL;
}
TEST(DeleteHeapSnapshot) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
- CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
+ CHECK_EQ(0, heap_profiler->GetSnapshotCount());
const v8::HeapSnapshot* s1 =
- v8::HeapProfiler::TakeSnapshot(v8_str("1"));
+ heap_profiler->TakeHeapSnapshot(v8_str("1"));
+
CHECK_NE(NULL, s1);
- CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
+ CHECK_EQ(1, heap_profiler->GetSnapshotCount());
unsigned uid1 = s1->GetUid();
- CHECK_EQ(s1, v8::HeapProfiler::FindSnapshot(uid1));
+ CHECK_EQ(s1, FindHeapSnapshot(heap_profiler, uid1));
const_cast<v8::HeapSnapshot*>(s1)->Delete();
- CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
- CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid1));
+ CHECK_EQ(0, heap_profiler->GetSnapshotCount());
+ CHECK_EQ(NULL, FindHeapSnapshot(heap_profiler, uid1));
const v8::HeapSnapshot* s2 =
- v8::HeapProfiler::TakeSnapshot(v8_str("2"));
+ heap_profiler->TakeHeapSnapshot(v8_str("2"));
CHECK_NE(NULL, s2);
- CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
+ CHECK_EQ(1, heap_profiler->GetSnapshotCount());
unsigned uid2 = s2->GetUid();
CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid2));
- CHECK_EQ(s2, v8::HeapProfiler::FindSnapshot(uid2));
+ CHECK_EQ(s2, FindHeapSnapshot(heap_profiler, uid2));
const v8::HeapSnapshot* s3 =
- v8::HeapProfiler::TakeSnapshot(v8_str("3"));
+ heap_profiler->TakeHeapSnapshot(v8_str("3"));
CHECK_NE(NULL, s3);
- CHECK_EQ(2, v8::HeapProfiler::GetSnapshotsCount());
+ CHECK_EQ(2, heap_profiler->GetSnapshotCount());
unsigned uid3 = s3->GetUid();
CHECK_NE(static_cast<int>(uid1), static_cast<int>(uid3));
- CHECK_EQ(s3, v8::HeapProfiler::FindSnapshot(uid3));
+ CHECK_EQ(s3, FindHeapSnapshot(heap_profiler, uid3));
const_cast<v8::HeapSnapshot*>(s2)->Delete();
- CHECK_EQ(1, v8::HeapProfiler::GetSnapshotsCount());
- CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid2));
- CHECK_EQ(s3, v8::HeapProfiler::FindSnapshot(uid3));
+ CHECK_EQ(1, heap_profiler->GetSnapshotCount());
+ CHECK_EQ(NULL, FindHeapSnapshot(heap_profiler, uid2));
+ CHECK_EQ(s3, FindHeapSnapshot(heap_profiler, uid3));
const_cast<v8::HeapSnapshot*>(s3)->Delete();
- CHECK_EQ(0, v8::HeapProfiler::GetSnapshotsCount());
- CHECK_EQ(NULL, v8::HeapProfiler::FindSnapshot(uid3));
+ CHECK_EQ(0, heap_profiler->GetSnapshotCount());
+ CHECK_EQ(NULL, FindHeapSnapshot(heap_profiler, uid3));
}
-TEST(DocumentURL) {
- v8::HandleScope scope;
+class NameResolver : public v8::HeapProfiler::ObjectNameResolver {
+ public:
+ virtual const char* GetName(v8::Handle<v8::Object> object) {
+ return "Global object name";
+ }
+};
+
+
+TEST(GlobalObjectName) {
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
CompileRun("document = { URL:\"abcdefgh\" };");
+ NameResolver name_resolver;
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("document"));
+ heap_profiler->TakeHeapSnapshot(v8_str("document"),
+ NULL,
+ &name_resolver);
+ CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
CHECK_NE(NULL, global);
- CHECK_EQ("Object / abcdefgh",
+ CHECK_EQ("Object / Global object name" ,
const_cast<i::HeapEntry*>(
reinterpret_cast<const i::HeapEntry*>(global))->name());
}
-TEST(DocumentWithException) {
- v8::HandleScope scope;
+TEST(GlobalObjectFields) {
LocalContext env;
-
- CompileRun(
- "this.__defineGetter__(\"document\", function() { throw new Error(); })");
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ CompileRun("obj = {};");
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("document"));
+ heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
+ CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
- CHECK_NE(NULL, global);
- CHECK_EQ("Object",
- const_cast<i::HeapEntry*>(
- reinterpret_cast<const i::HeapEntry*>(global))->name());
-}
-
-
-TEST(DocumentURLWithException) {
- v8::HandleScope scope;
- LocalContext env;
-
- CompileRun(
- "function URLWithException() {}\n"
- "URLWithException.prototype = { get URL() { throw new Error(); } };\n"
- "document = { URL: new URLWithException() };");
- const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("document"));
- const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
- CHECK_NE(NULL, global);
- CHECK_EQ("Object",
- const_cast<i::HeapEntry*>(
- reinterpret_cast<const i::HeapEntry*>(global))->name());
+ const v8::HeapGraphNode* builtins =
+ GetProperty(global, v8::HeapGraphEdge::kInternal, "builtins");
+ CHECK_NE(NULL, builtins);
+ const v8::HeapGraphNode* native_context =
+ GetProperty(global, v8::HeapGraphEdge::kInternal, "native_context");
+ CHECK_NE(NULL, native_context);
+ const v8::HeapGraphNode* global_context =
+ GetProperty(global, v8::HeapGraphEdge::kInternal, "global_context");
+ CHECK_NE(NULL, global_context);
+ const v8::HeapGraphNode* global_proxy =
+ GetProperty(global, v8::HeapGraphEdge::kInternal, "global_proxy");
+ CHECK_NE(NULL, global_proxy);
}
TEST(NoHandleLeaks) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
CompileRun("document = { URL:\"abcdefgh\" };");
v8::Handle<v8::String> name(v8_str("leakz"));
- int count_before = i::HandleScope::NumberOfHandles();
- v8::HeapProfiler::TakeSnapshot(name);
- int count_after = i::HandleScope::NumberOfHandles();
+ i::Isolate* isolate = CcTest::i_isolate();
+ int count_before = i::HandleScope::NumberOfHandles(isolate);
+ heap_profiler->TakeHeapSnapshot(name);
+ int count_after = i::HandleScope::NumberOfHandles(isolate);
CHECK_EQ(count_before, count_after);
}
TEST(NodesIteration) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("iteration"));
+ heap_profiler->TakeHeapSnapshot(v8_str("iteration"));
+ CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
CHECK_NE(NULL, global);
// Verify that we can find this object by iteration.
@@ -1115,74 +1764,78 @@
}
-TEST(GetHeapValue) {
- v8::HandleScope scope;
+TEST(GetHeapValueForNode) {
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
- CompileRun("a = { s_prop: \'value\', n_prop: 0.1 };");
+ CompileRun("a = { s_prop: \'value\', n_prop: \'value2\' };");
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("value"));
+ heap_profiler->TakeHeapSnapshot(v8_str("value"));
+ CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
- CHECK(global->GetHeapValue()->IsObject());
+ CHECK(heap_profiler->FindObjectById(global->GetId())->IsObject());
v8::Local<v8::Object> js_global =
env->Global()->GetPrototype().As<v8::Object>();
- CHECK(js_global == global->GetHeapValue());
+ CHECK(js_global == heap_profiler->FindObjectById(global->GetId()));
const v8::HeapGraphNode* obj = GetProperty(
- global, v8::HeapGraphEdge::kShortcut, "a");
- CHECK(obj->GetHeapValue()->IsObject());
+ global, v8::HeapGraphEdge::kProperty, "a");
+ CHECK(heap_profiler->FindObjectById(obj->GetId())->IsObject());
v8::Local<v8::Object> js_obj = js_global->Get(v8_str("a")).As<v8::Object>();
- CHECK(js_obj == obj->GetHeapValue());
+ CHECK(js_obj == heap_profiler->FindObjectById(obj->GetId()));
const v8::HeapGraphNode* s_prop =
GetProperty(obj, v8::HeapGraphEdge::kProperty, "s_prop");
v8::Local<v8::String> js_s_prop =
js_obj->Get(v8_str("s_prop")).As<v8::String>();
- CHECK(js_s_prop == s_prop->GetHeapValue());
+ CHECK(js_s_prop == heap_profiler->FindObjectById(s_prop->GetId()));
const v8::HeapGraphNode* n_prop =
GetProperty(obj, v8::HeapGraphEdge::kProperty, "n_prop");
- v8::Local<v8::Number> js_n_prop =
- js_obj->Get(v8_str("n_prop")).As<v8::Number>();
- CHECK(js_n_prop == n_prop->GetHeapValue());
+ v8::Local<v8::String> js_n_prop =
+ js_obj->Get(v8_str("n_prop")).As<v8::String>();
+ CHECK(js_n_prop == heap_profiler->FindObjectById(n_prop->GetId()));
}
TEST(GetHeapValueForDeletedObject) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
// It is impossible to delete a global property, so we are about to delete a
// property of the "a" object. Also, the "p" object can't be an empty one
// because the empty object is static and isn't actually deleted.
CompileRun("a = { p: { r: {} } };");
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("snapshot"));
+ heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
+ CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
const v8::HeapGraphNode* obj = GetProperty(
- global, v8::HeapGraphEdge::kShortcut, "a");
+ global, v8::HeapGraphEdge::kProperty, "a");
const v8::HeapGraphNode* prop = GetProperty(
obj, v8::HeapGraphEdge::kProperty, "p");
{
// Perform the check inside a nested local scope to avoid creating a
// reference to the object we are deleting.
- v8::HandleScope scope;
- CHECK(prop->GetHeapValue()->IsObject());
+ v8::HandleScope scope(env->GetIsolate());
+ CHECK(heap_profiler->FindObjectById(prop->GetId())->IsObject());
}
CompileRun("delete a.p;");
- CHECK(prop->GetHeapValue()->IsUndefined());
+ CHECK(heap_profiler->FindObjectById(prop->GetId()).IsEmpty());
}
static int StringCmp(const char* ref, i::String* act) {
i::SmartArrayPointer<char> s_act = act->ToCString();
- int result = strcmp(ref, *s_act);
+ int result = strcmp(ref, s_act.get());
if (result != 0)
- fprintf(stderr, "Expected: \"%s\", Actual: \"%s\"\n", ref, *s_act);
+ fprintf(stderr, "Expected: \"%s\", Actual: \"%s\"\n", ref, s_act.get());
return result;
}
TEST(GetConstructorName) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
CompileRun(
"function Constructor1() {};\n"
@@ -1210,12 +1863,16 @@
"Constructor2", i::V8HeapExplorer::GetConstructorName(*js_obj2)));
v8::Local<v8::Object> obj3 = js_global->Get(v8_str("obj3")).As<v8::Object>();
i::Handle<i::JSObject> js_obj3 = v8::Utils::OpenHandle(*obj3);
- CHECK_EQ(0, StringCmp(
- "Constructor3", i::V8HeapExplorer::GetConstructorName(*js_obj3)));
+ // TODO(verwaest): Restore to Constructor3 once supported by the
+ // heap-snapshot-generator.
+ CHECK_EQ(
+ 0, StringCmp("Object", i::V8HeapExplorer::GetConstructorName(*js_obj3)));
v8::Local<v8::Object> obj4 = js_global->Get(v8_str("obj4")).As<v8::Object>();
i::Handle<i::JSObject> js_obj4 = v8::Utils::OpenHandle(*obj4);
- CHECK_EQ(0, StringCmp(
- "Constructor4", i::V8HeapExplorer::GetConstructorName(*js_obj4)));
+ // TODO(verwaest): Restore to Constructor4 once supported by the
+ // heap-snapshot-generator.
+ CHECK_EQ(
+ 0, StringCmp("Object", i::V8HeapExplorer::GetConstructorName(*js_obj4)));
v8::Local<v8::Object> obj5 = js_global->Get(v8_str("obj5")).As<v8::Object>();
i::Handle<i::JSObject> js_obj5 = v8::Utils::OpenHandle(*obj5);
CHECK_EQ(0, StringCmp(
@@ -1227,9 +1884,10 @@
}
-TEST(FastCaseGetter) {
- v8::HandleScope scope;
+TEST(FastCaseAccessors) {
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
CompileRun("var obj1 = {};\n"
"obj1.__defineGetter__('propWithGetter', function Y() {\n"
@@ -1239,19 +1897,133 @@
" return this.value_ = value;\n"
"});\n");
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("fastCaseGetter"));
+ heap_profiler->TakeHeapSnapshot(v8_str("fastCaseAccessors"));
+ CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
CHECK_NE(NULL, global);
const v8::HeapGraphNode* obj1 =
- GetProperty(global, v8::HeapGraphEdge::kShortcut, "obj1");
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
CHECK_NE(NULL, obj1);
- const v8::HeapGraphNode* getterFunction =
- GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get-propWithGetter");
- CHECK_NE(NULL, getterFunction);
- const v8::HeapGraphNode* setterFunction =
- GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set-propWithSetter");
- CHECK_NE(NULL, setterFunction);
+ const v8::HeapGraphNode* func;
+ func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithGetter");
+ CHECK_NE(NULL, func);
+ func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithGetter");
+ CHECK_EQ(NULL, func);
+ func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithSetter");
+ CHECK_NE(NULL, func);
+ func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithSetter");
+ CHECK_EQ(NULL, func);
+}
+
+
+TEST(SlowCaseAccessors) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+
+ CompileRun("var obj1 = {};\n"
+ "for (var i = 0; i < 100; ++i) obj1['z' + i] = {};"
+ "obj1.__defineGetter__('propWithGetter', function Y() {\n"
+ " return 42;\n"
+ "});\n"
+ "obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
+ " return this.value_ = value;\n"
+ "});\n");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("slowCaseAccessors"));
+ CHECK(ValidateSnapshot(snapshot));
+
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ CHECK_NE(NULL, global);
+ const v8::HeapGraphNode* obj1 =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
+ CHECK_NE(NULL, obj1);
+ const v8::HeapGraphNode* func;
+ func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithGetter");
+ CHECK_NE(NULL, func);
+ func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithGetter");
+ CHECK_EQ(NULL, func);
+ func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set propWithSetter");
+ CHECK_NE(NULL, func);
+ func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get propWithSetter");
+ CHECK_EQ(NULL, func);
+}
+
+
+TEST(HiddenPropertiesFastCase) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+
+ CompileRun(
+ "function C(x) { this.a = this; this.b = x; }\n"
+ "c = new C(2012);\n");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("HiddenPropertiesFastCase1"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ const v8::HeapGraphNode* c =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "c");
+ CHECK_NE(NULL, c);
+ const v8::HeapGraphNode* hidden_props =
+ GetProperty(c, v8::HeapGraphEdge::kInternal, "hidden_properties");
+ CHECK_EQ(NULL, hidden_props);
+
+ v8::Handle<v8::Value> cHandle =
+ env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "c"));
+ CHECK(!cHandle.IsEmpty() && cHandle->IsObject());
+ cHandle->ToObject()->SetHiddenValue(v8_str("key"), v8_str("val"));
+
+ snapshot = heap_profiler->TakeHeapSnapshot(
+ v8_str("HiddenPropertiesFastCase2"));
+ CHECK(ValidateSnapshot(snapshot));
+ global = GetGlobalObject(snapshot);
+ c = GetProperty(global, v8::HeapGraphEdge::kProperty, "c");
+ CHECK_NE(NULL, c);
+ hidden_props = GetProperty(c, v8::HeapGraphEdge::kInternal,
+ "hidden_properties");
+ CHECK_NE(NULL, hidden_props);
+}
+
+
+TEST(AccessorInfo) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+
+ CompileRun("function foo(x) { }\n");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("AccessorInfoTest"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ const v8::HeapGraphNode* foo =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
+ CHECK_NE(NULL, foo);
+ const v8::HeapGraphNode* map =
+ GetProperty(foo, v8::HeapGraphEdge::kInternal, "map");
+ CHECK_NE(NULL, map);
+ const v8::HeapGraphNode* descriptors =
+ GetProperty(map, v8::HeapGraphEdge::kInternal, "descriptors");
+ CHECK_NE(NULL, descriptors);
+ const v8::HeapGraphNode* length_name =
+ GetProperty(descriptors, v8::HeapGraphEdge::kInternal, "2");
+ CHECK_NE(NULL, length_name);
+ CHECK_EQ("length", *v8::String::Utf8Value(length_name->GetName()));
+ const v8::HeapGraphNode* length_accessor =
+ GetProperty(descriptors, v8::HeapGraphEdge::kInternal, "4");
+ CHECK_NE(NULL, length_accessor);
+ CHECK_EQ("system / ExecutableAccessorInfo",
+ *v8::String::Utf8Value(length_accessor->GetName()));
+ const v8::HeapGraphNode* name =
+ GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "name");
+ CHECK_NE(NULL, name);
+ const v8::HeapGraphNode* getter =
+ GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "getter");
+ CHECK_NE(NULL, getter);
+ const v8::HeapGraphNode* setter =
+ GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "setter");
+ CHECK_NE(NULL, setter);
}
@@ -1265,104 +2037,770 @@
bool HasWeakGlobalHandle() {
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("weaks"));
+ heap_profiler->TakeHeapSnapshot(v8_str("weaks"));
+ CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* gc_roots = GetNode(
- snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)");
+ snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
CHECK_NE(NULL, gc_roots);
const v8::HeapGraphNode* global_handles = GetNode(
- gc_roots, v8::HeapGraphNode::kObject, "(Global handles)");
+ gc_roots, v8::HeapGraphNode::kSynthetic, "(Global handles)");
CHECK_NE(NULL, global_handles);
return HasWeakEdge(global_handles);
}
-static void PersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) {
- handle.Dispose();
+static void PersistentHandleCallback(
+ const v8::WeakCallbackData<v8::Object, v8::Persistent<v8::Object> >& data) {
+ data.GetParameter()->Reset();
+ delete data.GetParameter();
}
TEST(WeakGlobalHandle) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
CHECK(!HasWeakGlobalHandle());
- v8::Persistent<v8::Object> handle =
- v8::Persistent<v8::Object>::New(v8::Object::New());
- handle.MakeWeak(NULL, PersistentHandleCallback);
+ v8::Persistent<v8::Object> handle(env->GetIsolate(),
+ v8::Object::New(env->GetIsolate()));
+ handle.SetWeak(&handle, PersistentHandleCallback);
CHECK(HasWeakGlobalHandle());
}
-TEST(WeakGlobalContextRefs) {
- v8::HandleScope scope;
- LocalContext env;
-
- const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("weaks"));
- const v8::HeapGraphNode* gc_roots = GetNode(
- snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)");
- CHECK_NE(NULL, gc_roots);
- const v8::HeapGraphNode* global_handles = GetNode(
- gc_roots, v8::HeapGraphNode::kObject, "(Global handles)");
- CHECK_NE(NULL, global_handles);
- const v8::HeapGraphNode* global_context = GetNode(
- global_handles, v8::HeapGraphNode::kHidden, "system / GlobalContext");
- CHECK_NE(NULL, global_context);
- CHECK(HasWeakEdge(global_context));
-}
-
-
TEST(SfiAndJsFunctionWeakRefs) {
- v8::HandleScope scope;
LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
CompileRun(
"fun = (function (x) { return function () { return x + 1; } })(1);");
const v8::HeapSnapshot* snapshot =
- v8::HeapProfiler::TakeSnapshot(v8_str("fun"));
+ heap_profiler->TakeHeapSnapshot(v8_str("fun"));
+ CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
CHECK_NE(NULL, global);
const v8::HeapGraphNode* fun =
- GetProperty(global, v8::HeapGraphEdge::kShortcut, "fun");
- CHECK(HasWeakEdge(fun));
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "fun");
+ CHECK(!HasWeakEdge(fun));
const v8::HeapGraphNode* shared =
GetProperty(fun, v8::HeapGraphEdge::kInternal, "shared");
- CHECK(HasWeakEdge(shared));
+ CHECK(!HasWeakEdge(shared));
}
-TEST(PersistentHandleCount) {
- v8::HandleScope scope;
+TEST(NoDebugObjectInSnapshot) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+
+ CHECK(CcTest::i_isolate()->debug()->Load());
+ CompileRun("foo = {};");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* root = snapshot->GetRoot();
+ int globals_count = 0;
+ for (int i = 0; i < root->GetChildrenCount(); ++i) {
+ const v8::HeapGraphEdge* edge = root->GetChild(i);
+ if (edge->GetType() == v8::HeapGraphEdge::kShortcut) {
+ ++globals_count;
+ const v8::HeapGraphNode* global = edge->GetToNode();
+ const v8::HeapGraphNode* foo =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
+ CHECK_NE(NULL, foo);
+ }
+ }
+ CHECK_EQ(1, globals_count);
+}
+
+
+TEST(AllStrongGcRootsHaveNames) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+
+ CompileRun("foo = {};");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* gc_roots = GetNode(
+ snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
+ CHECK_NE(NULL, gc_roots);
+ const v8::HeapGraphNode* strong_roots = GetNode(
+ gc_roots, v8::HeapGraphNode::kSynthetic, "(Strong roots)");
+ CHECK_NE(NULL, strong_roots);
+ for (int i = 0; i < strong_roots->GetChildrenCount(); ++i) {
+ const v8::HeapGraphEdge* edge = strong_roots->GetChild(i);
+ CHECK_EQ(v8::HeapGraphEdge::kInternal, edge->GetType());
+ v8::String::Utf8Value name(edge->GetName());
+ CHECK(isalpha(**name));
+ }
+}
+
+
+TEST(NoRefsToNonEssentialEntries) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ CompileRun("global_object = {};\n");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ const v8::HeapGraphNode* global_object =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "global_object");
+ CHECK_NE(NULL, global_object);
+ const v8::HeapGraphNode* properties =
+ GetProperty(global_object, v8::HeapGraphEdge::kInternal, "properties");
+ CHECK_EQ(NULL, properties);
+ const v8::HeapGraphNode* elements =
+ GetProperty(global_object, v8::HeapGraphEdge::kInternal, "elements");
+ CHECK_EQ(NULL, elements);
+}
+
+
+TEST(MapHasDescriptorsAndTransitions) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ CompileRun("obj = { a: 10 };\n");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ const v8::HeapGraphNode* global_object =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "obj");
+ CHECK_NE(NULL, global_object);
+
+ const v8::HeapGraphNode* map =
+ GetProperty(global_object, v8::HeapGraphEdge::kInternal, "map");
+ CHECK_NE(NULL, map);
+ const v8::HeapGraphNode* own_descriptors = GetProperty(
+ map, v8::HeapGraphEdge::kInternal, "descriptors");
+ CHECK_NE(NULL, own_descriptors);
+ const v8::HeapGraphNode* own_transitions = GetProperty(
+ map, v8::HeapGraphEdge::kInternal, "transitions");
+ CHECK_EQ(NULL, own_transitions);
+}
+
+
+TEST(ManyLocalsInSharedContext) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ int num_objects = 6000;
+ CompileRun(
+ "var n = 6000;"
+ "var result = [];"
+ "result.push('(function outer() {');"
+ "for (var i = 0; i < n; i++) {"
+ " var f = 'function f_' + i + '() { ';"
+ " if (i > 0)"
+ " f += 'f_' + (i - 1) + '();';"
+ " f += ' }';"
+ " result.push(f);"
+ "}"
+ "result.push('return f_' + (n - 1) + ';');"
+ "result.push('})()');"
+ "var ok = eval(result.join('\\n'));");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
+ CHECK(ValidateSnapshot(snapshot));
+
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ CHECK_NE(NULL, global);
+ const v8::HeapGraphNode* ok_object =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "ok");
+ CHECK_NE(NULL, ok_object);
+ const v8::HeapGraphNode* context_object =
+ GetProperty(ok_object, v8::HeapGraphEdge::kInternal, "context");
+ CHECK_NE(NULL, context_object);
+ // Check the objects are not duplicated in the context.
+ CHECK_EQ(v8::internal::Context::MIN_CONTEXT_SLOTS + num_objects - 1,
+ context_object->GetChildrenCount());
+ // Check all the objects have got their names.
+ // ... well check just every 15th because otherwise it's too slow in debug.
+ for (int i = 0; i < num_objects - 1; i += 15) {
+ i::EmbeddedVector<char, 100> var_name;
+ i::SNPrintF(var_name, "f_%d", i);
+ const v8::HeapGraphNode* f_object = GetProperty(
+ context_object, v8::HeapGraphEdge::kContextVariable, var_name.start());
+ CHECK_NE(NULL, f_object);
+ }
+}
+
+
+TEST(AllocationSitesAreVisible) {
+ LocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
+ v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
+ CompileRun(
+ "fun = function () { var a = [3, 2, 1]; return a; }\n"
+ "fun();");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
+ CHECK(ValidateSnapshot(snapshot));
+
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ CHECK_NE(NULL, global);
+ const v8::HeapGraphNode* fun_code =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "fun");
+ CHECK_NE(NULL, fun_code);
+ const v8::HeapGraphNode* literals =
+ GetProperty(fun_code, v8::HeapGraphEdge::kInternal, "literals");
+ CHECK_NE(NULL, literals);
+ CHECK_EQ(v8::HeapGraphNode::kArray, literals->GetType());
+ CHECK_EQ(2, literals->GetChildrenCount());
+
+ // The second value in the literals array should be the boilerplate,
+ // after an AllocationSite.
+ const v8::HeapGraphEdge* prop = literals->GetChild(1);
+ const v8::HeapGraphNode* allocation_site = prop->GetToNode();
+ v8::String::Utf8Value name(allocation_site->GetName());
+ CHECK_EQ("system / AllocationSite", *name);
+ const v8::HeapGraphNode* transition_info =
+ GetProperty(allocation_site, v8::HeapGraphEdge::kInternal,
+ "transition_info");
+ CHECK_NE(NULL, transition_info);
+
+ const v8::HeapGraphNode* elements =
+ GetProperty(transition_info, v8::HeapGraphEdge::kInternal,
+ "elements");
+ CHECK_NE(NULL, elements);
+ CHECK_EQ(v8::HeapGraphNode::kArray, elements->GetType());
+ CHECK_EQ(v8::internal::FixedArray::SizeFor(3),
+ static_cast<int>(elements->GetShallowSize()));
+
+ v8::Handle<v8::Value> array_val =
+ heap_profiler->FindObjectById(transition_info->GetId());
+ CHECK(array_val->IsArray());
+ v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(array_val);
+ // Verify the array is "a" in the code above.
+ CHECK_EQ(3, array->Length());
+ CHECK_EQ(v8::Integer::New(isolate, 3),
+ array->Get(v8::Integer::New(isolate, 0)));
+ CHECK_EQ(v8::Integer::New(isolate, 2),
+ array->Get(v8::Integer::New(isolate, 1)));
+ CHECK_EQ(v8::Integer::New(isolate, 1),
+ array->Get(v8::Integer::New(isolate, 2)));
+}
+
+
+TEST(JSFunctionHasCodeLink) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ CompileRun("function foo(x, y) { return x + y; }\n");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ const v8::HeapGraphNode* foo_func =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
+ CHECK_NE(NULL, foo_func);
+ const v8::HeapGraphNode* code =
+ GetProperty(foo_func, v8::HeapGraphEdge::kInternal, "code");
+ CHECK_NE(NULL, code);
+}
+
+
+static const v8::HeapGraphNode* GetNodeByPath(const v8::HeapSnapshot* snapshot,
+ const char* path[],
+ int depth) {
+ const v8::HeapGraphNode* node = snapshot->GetRoot();
+ for (int current_depth = 0; current_depth < depth; ++current_depth) {
+ int i, count = node->GetChildrenCount();
+ for (i = 0; i < count; ++i) {
+ const v8::HeapGraphEdge* edge = node->GetChild(i);
+ const v8::HeapGraphNode* to_node = edge->GetToNode();
+ v8::String::Utf8Value edge_name(edge->GetName());
+ v8::String::Utf8Value node_name(to_node->GetName());
+ i::EmbeddedVector<char, 100> name;
+ i::SNPrintF(name, "%s::%s", *edge_name, *node_name);
+ if (strstr(name.start(), path[current_depth])) {
+ node = to_node;
+ break;
+ }
+ }
+ if (i == count) return NULL;
+ }
+ return node;
+}
+
+
+TEST(CheckCodeNames) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ CompileRun("var a = 1.1;");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("CheckCodeNames"));
+ CHECK(ValidateSnapshot(snapshot));
+
+ const char* stub_path[] = {
+ "::(GC roots)",
+ "::(Strong roots)",
+ "code_stubs::",
+ "::(ArraySingleArgumentConstructorStub code)"
+ };
+ const v8::HeapGraphNode* node = GetNodeByPath(snapshot,
+ stub_path, arraysize(stub_path));
+ CHECK_NE(NULL, node);
+
+ const char* builtin_path1[] = {
+ "::(GC roots)",
+ "::(Builtins)",
+ "::(KeyedLoadIC_Generic builtin)"
+ };
+ node = GetNodeByPath(snapshot, builtin_path1, arraysize(builtin_path1));
+ CHECK_NE(NULL, node);
+
+ const char* builtin_path2[] = {"::(GC roots)", "::(Builtins)",
+ "::(CompileLazy builtin)"};
+ node = GetNodeByPath(snapshot, builtin_path2, arraysize(builtin_path2));
+ CHECK_NE(NULL, node);
+ v8::String::Utf8Value node_name(node->GetName());
+ CHECK_EQ("(CompileLazy builtin)", *node_name);
+}
+
+
+static const char* record_trace_tree_source =
+"var topFunctions = [];\n"
+"var global = this;\n"
+"function generateFunctions(width, depth) {\n"
+" var script = [];\n"
+" for (var i = 0; i < width; i++) {\n"
+" for (var j = 0; j < depth; j++) {\n"
+" script.push('function f_' + i + '_' + j + '(x) {\\n');\n"
+" script.push(' try {\\n');\n"
+" if (j < depth-2) {\n"
+" script.push(' return f_' + i + '_' + (j+1) + '(x+1);\\n');\n"
+" } else if (j == depth - 2) {\n"
+" script.push(' return new f_' + i + '_' + (depth - 1) + '();\\n');\n"
+" } else if (j == depth - 1) {\n"
+" script.push(' this.ts = Date.now();\\n');\n"
+" }\n"
+" script.push(' } catch (e) {}\\n');\n"
+" script.push('}\\n');\n"
+" \n"
+" }\n"
+" }\n"
+" var script = script.join('');\n"
+" // throw script;\n"
+" global.eval(script);\n"
+" for (var i = 0; i < width; i++) {\n"
+" topFunctions.push(this['f_' + i + '_0']);\n"
+" }\n"
+"}\n"
+"\n"
+"var width = 3;\n"
+"var depth = 3;\n"
+"generateFunctions(width, depth);\n"
+"var instances = [];\n"
+"function start() {\n"
+" for (var i = 0; i < width; i++) {\n"
+" instances.push(topFunctions[i](0));\n"
+" }\n"
+"}\n"
+"\n"
+"for (var i = 0; i < 100; i++) start();\n";
+
+
+static AllocationTraceNode* FindNode(
+ AllocationTracker* tracker, const Vector<const char*>& names) {
+ AllocationTraceNode* node = tracker->trace_tree()->root();
+ for (int i = 0; node != NULL && i < names.length(); i++) {
+ const char* name = names[i];
+ Vector<AllocationTraceNode*> children = node->children();
+ node = NULL;
+ for (int j = 0; j < children.length(); j++) {
+ unsigned index = children[j]->function_info_index();
+ AllocationTracker::FunctionInfo* info =
+ tracker->function_info_list()[index];
+ if (info && strcmp(info->name, name) == 0) {
+ node = children[j];
+ break;
+ }
+ }
+ }
+ return node;
+}
+
+
+TEST(ArrayGrowLeftTrim) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ heap_profiler->StartTrackingHeapObjects(true);
+
+ CompileRun(
+ "var a = [];\n"
+ "for (var i = 0; i < 5; ++i)\n"
+ " a[i] = i;\n"
+ "for (var i = 0; i < 3; ++i)\n"
+ " a.shift();\n");
+
+ const char* names[] = {""};
+ AllocationTracker* tracker =
+ reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
+ CHECK_NE(NULL, tracker);
+ // Resolve all function locations.
+ tracker->PrepareForSerialization();
+ // Print for better diagnostics in case of failure.
+ tracker->trace_tree()->Print(tracker);
+
+ AllocationTraceNode* node =
+ FindNode(tracker, Vector<const char*>(names, arraysize(names)));
+ CHECK_NE(NULL, node);
+ CHECK_GE(node->allocation_count(), 2);
+ CHECK_GE(node->allocation_size(), 4 * 5);
+ heap_profiler->StopTrackingHeapObjects();
+}
+
+
+TEST(TrackHeapAllocations) {
+ v8::HandleScope scope(v8::Isolate::GetCurrent());
LocalContext env;
- // V8 also uses global handles internally, so we can't test for an absolute
- // number.
- int global_handle_count = v8::HeapProfiler::GetPersistentHandleCount();
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ heap_profiler->StartTrackingHeapObjects(true);
- // Create some persistent handles.
- v8::Persistent<v8::String> p_AAA =
- v8::Persistent<v8::String>::New(v8_str("AAA"));
- CHECK_EQ(global_handle_count + 1,
- v8::HeapProfiler::GetPersistentHandleCount());
- v8::Persistent<v8::String> p_BBB =
- v8::Persistent<v8::String>::New(v8_str("BBB"));
- CHECK_EQ(global_handle_count + 2,
- v8::HeapProfiler::GetPersistentHandleCount());
- v8::Persistent<v8::String> p_CCC =
- v8::Persistent<v8::String>::New(v8_str("CCC"));
- CHECK_EQ(global_handle_count + 3,
- v8::HeapProfiler::GetPersistentHandleCount());
+ CompileRun(record_trace_tree_source);
- // Dipose the persistent handles in a different order.
- p_AAA.Dispose();
- CHECK_EQ(global_handle_count + 2,
- v8::HeapProfiler::GetPersistentHandleCount());
- p_CCC.Dispose();
- CHECK_EQ(global_handle_count + 1,
- v8::HeapProfiler::GetPersistentHandleCount());
- p_BBB.Dispose();
- CHECK_EQ(global_handle_count, v8::HeapProfiler::GetPersistentHandleCount());
+ AllocationTracker* tracker =
+ reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
+ CHECK_NE(NULL, tracker);
+ // Resolve all function locations.
+ tracker->PrepareForSerialization();
+ // Print for better diagnostics in case of failure.
+ tracker->trace_tree()->Print(tracker);
+
+ const char* names[] = {"", "start", "f_0_0", "f_0_1", "f_0_2"};
+ AllocationTraceNode* node =
+ FindNode(tracker, Vector<const char*>(names, arraysize(names)));
+ CHECK_NE(NULL, node);
+ CHECK_GE(node->allocation_count(), 100);
+ CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
+ heap_profiler->StopTrackingHeapObjects();
+}
+
+
+static const char* inline_heap_allocation_source =
+"function f_0(x) {\n"
+" return f_1(x+1);\n"
+"}\n"
+"%NeverOptimizeFunction(f_0);\n"
+"function f_1(x) {\n"
+" return new f_2(x+1);\n"
+"}\n"
+"function f_2(x) {\n"
+" this.foo = x;\n"
+"}\n"
+"var instances = [];\n"
+"function start() {\n"
+" instances.push(f_0(0));\n"
+"}\n"
+"\n"
+"for (var i = 0; i < 100; i++) start();\n";
+
+
+TEST(TrackBumpPointerAllocations) {
+ i::FLAG_allow_natives_syntax = true;
+ v8::HandleScope scope(v8::Isolate::GetCurrent());
+ LocalContext env;
+
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ const char* names[] = {"", "start", "f_0", "f_1"};
+ // First check that normally all allocations are recorded.
+ {
+ heap_profiler->StartTrackingHeapObjects(true);
+
+ CompileRun(inline_heap_allocation_source);
+
+ AllocationTracker* tracker =
+ reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
+ CHECK_NE(NULL, tracker);
+ // Resolve all function locations.
+ tracker->PrepareForSerialization();
+ // Print for better diagnostics in case of failure.
+ tracker->trace_tree()->Print(tracker);
+
+ AllocationTraceNode* node =
+ FindNode(tracker, Vector<const char*>(names, arraysize(names)));
+ CHECK_NE(NULL, node);
+ CHECK_GE(node->allocation_count(), 100);
+ CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
+ heap_profiler->StopTrackingHeapObjects();
+ }
+
+ {
+ heap_profiler->StartTrackingHeapObjects(true);
+
+ // Now check that not all allocations are tracked if we manually reenable
+ // inline allocations.
+ CHECK(CcTest::heap()->inline_allocation_disabled());
+ CcTest::heap()->EnableInlineAllocation();
+
+ CompileRun(inline_heap_allocation_source);
+
+ AllocationTracker* tracker =
+ reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
+ CHECK_NE(NULL, tracker);
+ // Resolve all function locations.
+ tracker->PrepareForSerialization();
+ // Print for better diagnostics in case of failure.
+ tracker->trace_tree()->Print(tracker);
+
+ AllocationTraceNode* node =
+ FindNode(tracker, Vector<const char*>(names, arraysize(names)));
+ CHECK_NE(NULL, node);
+ CHECK_LT(node->allocation_count(), 100);
+
+ CcTest::heap()->DisableInlineAllocation();
+ heap_profiler->StopTrackingHeapObjects();
+ }
+}
+
+
+TEST(TrackV8ApiAllocation) {
+ v8::HandleScope scope(v8::Isolate::GetCurrent());
+ LocalContext env;
+
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ const char* names[] = { "(V8 API)" };
+ heap_profiler->StartTrackingHeapObjects(true);
+
+ v8::Handle<v8::Object> o1 = v8::Object::New(env->GetIsolate());
+ o1->Clone();
+
+ AllocationTracker* tracker =
+ reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
+ CHECK_NE(NULL, tracker);
+ // Resolve all function locations.
+ tracker->PrepareForSerialization();
+ // Print for better diagnostics in case of failure.
+ tracker->trace_tree()->Print(tracker);
+
+ AllocationTraceNode* node =
+ FindNode(tracker, Vector<const char*>(names, arraysize(names)));
+ CHECK_NE(NULL, node);
+ CHECK_GE(node->allocation_count(), 2);
+ CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
+ heap_profiler->StopTrackingHeapObjects();
+}
+
+
+TEST(ArrayBufferAndArrayBufferView) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ CompileRun("arr1 = new Uint32Array(100);\n");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ const v8::HeapGraphNode* arr1_obj =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "arr1");
+ CHECK_NE(NULL, arr1_obj);
+ const v8::HeapGraphNode* arr1_buffer =
+ GetProperty(arr1_obj, v8::HeapGraphEdge::kInternal, "buffer");
+ CHECK_NE(NULL, arr1_buffer);
+ const v8::HeapGraphNode* first_view =
+ GetProperty(arr1_buffer, v8::HeapGraphEdge::kWeak, "weak_first_view");
+ CHECK_NE(NULL, first_view);
+ const v8::HeapGraphNode* backing_store =
+ GetProperty(arr1_buffer, v8::HeapGraphEdge::kInternal, "backing_store");
+ CHECK_NE(NULL, backing_store);
+ CHECK_EQ(400, static_cast<int>(backing_store->GetShallowSize()));
+}
+
+
+static int GetRetainersCount(const v8::HeapSnapshot* snapshot,
+ const v8::HeapGraphNode* node) {
+ int count = 0;
+ for (int i = 0, l = snapshot->GetNodesCount(); i < l; ++i) {
+ const v8::HeapGraphNode* parent = snapshot->GetNode(i);
+ for (int j = 0, l2 = parent->GetChildrenCount(); j < l2; ++j) {
+ if (parent->GetChild(j)->GetToNode() == node) {
+ ++count;
+ }
+ }
+ }
+ return count;
+}
+
+
+TEST(ArrayBufferSharedBackingStore) {
+ LocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope handle_scope(isolate);
+ v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
+
+ v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 1024);
+ CHECK_EQ(1024, static_cast<int>(ab->ByteLength()));
+ CHECK(!ab->IsExternal());
+ v8::ArrayBuffer::Contents ab_contents = ab->Externalize();
+ CHECK(ab->IsExternal());
+
+ CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
+ void* data = ab_contents.Data();
+ DCHECK(data != NULL);
+ v8::Local<v8::ArrayBuffer> ab2 =
+ v8::ArrayBuffer::New(isolate, data, ab_contents.ByteLength());
+ CHECK(ab2->IsExternal());
+ env->Global()->Set(v8_str("ab1"), ab);
+ env->Global()->Set(v8_str("ab2"), ab2);
+
+ v8::Handle<v8::Value> result = CompileRun("ab2.byteLength");
+ CHECK_EQ(1024, result->Int32Value());
+
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ const v8::HeapGraphNode* ab1_node =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "ab1");
+ CHECK_NE(NULL, ab1_node);
+ const v8::HeapGraphNode* ab1_data =
+ GetProperty(ab1_node, v8::HeapGraphEdge::kInternal, "backing_store");
+ CHECK_NE(NULL, ab1_data);
+ const v8::HeapGraphNode* ab2_node =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "ab2");
+ CHECK_NE(NULL, ab2_node);
+ const v8::HeapGraphNode* ab2_data =
+ GetProperty(ab2_node, v8::HeapGraphEdge::kInternal, "backing_store");
+ CHECK_NE(NULL, ab2_data);
+ CHECK_EQ(ab1_data, ab2_data);
+ CHECK_EQ(2, GetRetainersCount(snapshot, ab1_data));
+ free(data);
+}
+
+
+TEST(BoxObject) {
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope scope(isolate);
+ LocalContext env;
+ v8::Handle<v8::Object> global_proxy = env->Global();
+ v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
+
+ i::Factory* factory = CcTest::i_isolate()->factory();
+ i::Handle<i::String> string = factory->NewStringFromStaticChars("string");
+ i::Handle<i::Object> box = factory->NewBox(string);
+ global->Set(0, v8::ToApiHandle<v8::Object>(box));
+
+ v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
+ const v8::HeapGraphNode* box_node =
+ GetProperty(global_node, v8::HeapGraphEdge::kElement, "0");
+ CHECK_NE(NULL, box_node);
+ v8::String::Utf8Value box_node_name(box_node->GetName());
+ CHECK_EQ("system / Box", *box_node_name);
+ const v8::HeapGraphNode* box_value =
+ GetProperty(box_node, v8::HeapGraphEdge::kInternal, "value");
+ CHECK_NE(NULL, box_value);
+}
+
+
+TEST(WeakContainers) {
+ i::FLAG_allow_natives_syntax = true;
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ if (!CcTest::i_isolate()->use_crankshaft()) return;
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ CompileRun(
+ "function foo(a) { return a.x; }\n"
+ "obj = {x : 123};\n"
+ "foo(obj);\n"
+ "foo(obj);\n"
+ "%OptimizeFunctionOnNextCall(foo);\n"
+ "foo(obj);\n");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ const v8::HeapGraphNode* obj =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "obj");
+ CHECK_NE(NULL, obj);
+ const v8::HeapGraphNode* map =
+ GetProperty(obj, v8::HeapGraphEdge::kInternal, "map");
+ CHECK_NE(NULL, map);
+ const v8::HeapGraphNode* dependent_code =
+ GetProperty(map, v8::HeapGraphEdge::kInternal, "dependent_code");
+ if (!dependent_code) return;
+ int count = dependent_code->GetChildrenCount();
+ CHECK_NE(0, count);
+ for (int i = 0; i < count; ++i) {
+ const v8::HeapGraphEdge* prop = dependent_code->GetChild(i);
+ CHECK_EQ(v8::HeapGraphEdge::kWeak, prop->GetType());
+ }
+}
+
+
+static inline i::Address ToAddress(int n) {
+ return reinterpret_cast<i::Address>(n);
+}
+
+
+TEST(AddressToTraceMap) {
+ i::AddressToTraceMap map;
+
+ CHECK_EQ(0, map.GetTraceNodeId(ToAddress(150)));
+
+ // [0x100, 0x200) -> 1
+ map.AddRange(ToAddress(0x100), 0x100, 1U);
+ CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x50)));
+ CHECK_EQ(1, map.GetTraceNodeId(ToAddress(0x100)));
+ CHECK_EQ(1, map.GetTraceNodeId(ToAddress(0x150)));
+ CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x100 + 0x100)));
+ CHECK_EQ(1, static_cast<int>(map.size()));
+
+ // [0x100, 0x200) -> 1, [0x200, 0x300) -> 2
+ map.AddRange(ToAddress(0x200), 0x100, 2U);
+ CHECK_EQ(2, map.GetTraceNodeId(ToAddress(0x2a0)));
+ CHECK_EQ(2, static_cast<int>(map.size()));
+
+ // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2
+ map.AddRange(ToAddress(0x180), 0x100, 3U);
+ CHECK_EQ(1, map.GetTraceNodeId(ToAddress(0x17F)));
+ CHECK_EQ(2, map.GetTraceNodeId(ToAddress(0x280)));
+ CHECK_EQ(3, map.GetTraceNodeId(ToAddress(0x180)));
+ CHECK_EQ(3, static_cast<int>(map.size()));
+
+ // [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2,
+ // [0x400, 0x500) -> 4
+ map.AddRange(ToAddress(0x400), 0x100, 4U);
+ CHECK_EQ(1, map.GetTraceNodeId(ToAddress(0x17F)));
+ CHECK_EQ(2, map.GetTraceNodeId(ToAddress(0x280)));
+ CHECK_EQ(3, map.GetTraceNodeId(ToAddress(0x180)));
+ CHECK_EQ(4, map.GetTraceNodeId(ToAddress(0x450)));
+ CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x500)));
+ CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x350)));
+ CHECK_EQ(4, static_cast<int>(map.size()));
+
+ // [0x100, 0x180) -> 1, [0x180, 0x200) -> 3, [0x200, 0x600) -> 5
+ map.AddRange(ToAddress(0x200), 0x400, 5U);
+ CHECK_EQ(5, map.GetTraceNodeId(ToAddress(0x200)));
+ CHECK_EQ(5, map.GetTraceNodeId(ToAddress(0x400)));
+ CHECK_EQ(3, static_cast<int>(map.size()));
+
+ // [0x100, 0x180) -> 1, [0x180, 0x200) -> 7, [0x200, 0x600) ->5
+ map.AddRange(ToAddress(0x180), 0x80, 6U);
+ map.AddRange(ToAddress(0x180), 0x80, 7U);
+ CHECK_EQ(7, map.GetTraceNodeId(ToAddress(0x180)));
+ CHECK_EQ(5, map.GetTraceNodeId(ToAddress(0x200)));
+ CHECK_EQ(3, static_cast<int>(map.size()));
+
+ map.Clear();
+ CHECK_EQ(0, static_cast<int>(map.size()));
+ CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x400)));
}