Revision: 4864
Author: [email protected]
Date: Tue Jun 15 04:44:07 2010
Log: Heap profiler: publish API and add test.
Review URL: http://codereview.chromium.org/2822009
http://code.google.com/p/v8/source/detail?r=4864
Modified:
/branches/bleeding_edge/include/v8-profiler.h
/branches/bleeding_edge/src/api.cc
/branches/bleeding_edge/src/heap-profiler.cc
/branches/bleeding_edge/src/heap-profiler.h
/branches/bleeding_edge/src/profile-generator.cc
/branches/bleeding_edge/src/profile-generator.h
/branches/bleeding_edge/src/v8.cc
/branches/bleeding_edge/test/cctest/test-heap-profiler.cc
=======================================
--- /branches/bleeding_edge/include/v8-profiler.h Tue May 18 07:19:33 2010
+++ /branches/bleeding_edge/include/v8-profiler.h Tue Jun 15 04:44:07 2010
@@ -184,6 +184,145 @@
};
+class HeapGraphNode;
+
+
+/**
+ * HeapSnapshotEdge represents a directed connection between heap
+ * graph nodes: from retaners to retained nodes.
+ */
+class V8EXPORT HeapGraphEdge {
+ public:
+ enum Type {
+ CONTEXT_VARIABLE = 0, // A variable from a function context.
+ ELEMENT = 1, // An element of an array.
+ PROPERTY = 2 // A named object property.
+ };
+
+ /** Returns edge type (see HeapGraphEdge::Type). */
+ Type GetType() const;
+
+ /**
+ * Returns edge name. This can be a variable name, an element index, or
+ * a property name.
+ */
+ Handle<Value> GetName() const;
+
+ /** Returns origin node. */
+ const HeapGraphNode* GetFromNode() const;
+
+ /** Returns destination node. */
+ const HeapGraphNode* GetToNode() const;
+};
+
+
+class V8EXPORT HeapGraphPath {
+ public:
+ /** Returns the number of edges in the path. */
+ int GetEdgesCount() const;
+
+ /** Returns an edge from the path. */
+ const HeapGraphEdge* GetEdge(int index) const;
+
+ /** Returns origin node. */
+ const HeapGraphNode* GetFromNode() const;
+
+ /** Returns destination node. */
+ const HeapGraphNode* GetToNode() const;
+};
+
+
+/**
+ * HeapGraphNode represents a node in a heap graph.
+ */
+class V8EXPORT HeapGraphNode {
+ public:
+ enum Type {
+ INTERNAL = 0, // Internal node, a virtual one, for housekeeping.
+ ARRAY = 1, // An array of elements.
+ STRING = 2, // A string.
+ OBJECT = 3, // A JS object (except for arrays and strings).
+ CODE = 4, // Compiled code.
+ CLOSURE = 5 // Function closure.
+ };
+
+ /** Returns node type (see HeapGraphNode::Type). */
+ Type GetType() const;
+
+ /**
+ * Returns node name. Depending on node's type this can be the name
+ * of the constructor (for objects), the name of the function (for
+ * closures), string value, or an empty string (for compiled code).
+ */
+ Handle<String> GetName() const;
+
+ /** Returns node's own size, in bytes. */
+ int GetSelfSize() const;
+
+ /** Returns node's network (self + reachable nodes) size, in bytes. */
+ int GetTotalSize() const;
+
+ /**
+ * Returns node's private size, in bytes. That is, the size of memory
+ * that will be reclaimed having this node collected.
+ */
+ int GetPrivateSize() const;
+
+ /** Returns child nodes count of the node. */
+ int GetChildrenCount() const;
+
+ /** Retrieves a child by index. */
+ const HeapGraphEdge* GetChild(int index) const;
+
+ /** Returns retainer nodes count of the node. */
+ int GetRetainersCount() const;
+
+ /** Returns a retainer by index. */
+ const HeapGraphEdge* GetRetainer(int index) const;
+
+ /** Returns the number of simple retaining paths from the root to the
node. */
+ int GetRetainingPathsCount() const;
+
+ /** Returns a retaining path by index. */
+ const HeapGraphPath* GetRetainingPath(int index) const;
+};
+
+
+/**
+ * HeapSnapshots record the state of the JS heap at some moment.
+ */
+class V8EXPORT HeapSnapshot {
+ public:
+ /** Returns heap snapshot UID (assigned by the profiler.) */
+ unsigned GetUid() const;
+
+ /** Returns heap snapshot title. */
+ Handle<String> GetTitle() const;
+
+ /** Returns the root node of the heap graph. */
+ const HeapGraphNode* GetHead() const;
+};
+
+
+/**
+ * Interface for controlling heap profiling.
+ */
+class V8EXPORT HeapProfiler {
+ public:
+ /** Returns the number of snapshots taken. */
+ static int GetSnapshotsCount();
+
+ /** Returns a snapshot by index. */
+ static const HeapSnapshot* GetSnapshot(int index);
+
+ /** Returns a profile by uid. */
+ static const HeapSnapshot* FindSnapshot(unsigned uid);
+
+ /** Takes a heap snapshot and returns it. Title may be an empty string.
*/
+ static const HeapSnapshot* TakeSnapshot(Handle<String> title);
+};
+
+
} // namespace v8
=======================================
--- /branches/bleeding_edge/src/api.cc Mon Jun 14 06:09:27 2010
+++ /branches/bleeding_edge/src/api.cc Tue Jun 15 04:44:07 2010
@@ -34,6 +34,7 @@
#include "debug.h"
#include "execution.h"
#include "global-handles.h"
+#include "heap-profiler.h"
#include "messages.h"
#include "platform.h"
#include "profile-generator-inl.h"
@@ -4445,6 +4446,196 @@
security_token.IsEmpty() ? NULL :
*Utils::OpenHandle(*security_token),
*Utils::OpenHandle(*title)));
}
+
+
+HeapGraphEdge::Type HeapGraphEdge::GetType() const {
+ IsDeadCheck("v8::HeapGraphEdge::GetType");
+ return static_cast<HeapGraphEdge::Type>(
+ reinterpret_cast<const i::HeapGraphEdge*>(this)->type());
+}
+
+
+Handle<Value> HeapGraphEdge::GetName() const {
+ IsDeadCheck("v8::HeapGraphEdge::GetName");
+ const i::HeapGraphEdge* edge =
+ reinterpret_cast<const i::HeapGraphEdge*>(this);
+ switch (edge->type()) {
+ case i::HeapGraphEdge::CONTEXT_VARIABLE:
+ case i::HeapGraphEdge::PROPERTY:
+ return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol(
+ edge->name())));
+ case i::HeapGraphEdge::ELEMENT:
+ return Handle<Number>(ToApi<Number>(i::Factory::NewNumberFromInt(
+ edge->index())));
+ default: UNREACHABLE();
+ }
+ return ImplementationUtilities::Undefined();
+}
+
+
+const HeapGraphNode* HeapGraphEdge::GetFromNode() const {
+ IsDeadCheck("v8::HeapGraphEdge::GetFromNode");
+ const i::HeapEntry* from =
+ reinterpret_cast<const i::HeapGraphEdge*>(this)->from();
+ return reinterpret_cast<const HeapGraphNode*>(from);
+}
+
+
+const HeapGraphNode* HeapGraphEdge::GetToNode() const {
+ IsDeadCheck("v8::HeapGraphEdge::GetToNode");
+ const i::HeapEntry* to =
+ reinterpret_cast<const i::HeapGraphEdge*>(this)->to();
+ return reinterpret_cast<const HeapGraphNode*>(to);
+}
+
+
+int HeapGraphPath::GetEdgesCount() const {
+ return reinterpret_cast<const i::HeapGraphPath*>(this)->path()->length();
+}
+
+
+const HeapGraphEdge* HeapGraphPath::GetEdge(int index) const {
+ return reinterpret_cast<const HeapGraphEdge*>(
+ reinterpret_cast<const i::HeapGraphPath*>(this)->path()->at(index));
+}
+
+
+const HeapGraphNode* HeapGraphPath::GetFromNode() const {
+ return GetEdgesCount() > 0 ? GetEdge(0)->GetFromNode() : NULL;
+}
+
+
+const HeapGraphNode* HeapGraphPath::GetToNode() const {
+ const int count = GetEdgesCount();
+ return count > 0 ? GetEdge(count - 1)->GetToNode() : NULL;
+}
+
+
+HeapGraphNode::Type HeapGraphNode::GetType() const {
+ IsDeadCheck("v8::HeapGraphNode::GetType");
+ return static_cast<HeapGraphNode::Type>(
+ reinterpret_cast<const i::HeapEntry*>(this)->type());
+}
+
+
+Handle<String> HeapGraphNode::GetName() const {
+ IsDeadCheck("v8::HeapGraphNode::GetName");
+ return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol(
+ reinterpret_cast<const i::HeapEntry*>(this)->name())));
+}
+
+
+int HeapGraphNode::GetSelfSize() const {
+ IsDeadCheck("v8::HeapGraphNode::GetSelfSize");
+ return reinterpret_cast<const i::HeapEntry*>(this)->self_size();
+}
+
+
+int HeapGraphNode::GetTotalSize() const {
+ IsDeadCheck("v8::HeapSnapshot::GetHead");
+ return const_cast<i::HeapEntry*>(
+ reinterpret_cast<const i::HeapEntry*>(this))->TotalSize();
+}
+
+
+int HeapGraphNode::GetPrivateSize() const {
+ IsDeadCheck("v8::HeapSnapshot::GetPrivateSize");
+ return const_cast<i::HeapEntry*>(
+ reinterpret_cast<const i::HeapEntry*>(this))->NonSharedTotalSize();
+}
+
+
+int HeapGraphNode::GetChildrenCount() const {
+ IsDeadCheck("v8::HeapSnapshot::GetChildrenCount");
+ return reinterpret_cast<const i::HeapEntry*>(this)->children()->length();
+}
+
+
+const HeapGraphEdge* HeapGraphNode::GetChild(int index) const {
+ IsDeadCheck("v8::HeapSnapshot::GetChild");
+ return reinterpret_cast<const HeapGraphEdge*>(
+ reinterpret_cast<const i::HeapEntry*>(this)->children()->at(index));
+}
+
+
+int HeapGraphNode::GetRetainersCount() const {
+ IsDeadCheck("v8::HeapSnapshot::GetRetainersCount");
+ return reinterpret_cast<const
i::HeapEntry*>(this)->retainers()->length();
+}
+
+
+const HeapGraphEdge* HeapGraphNode::GetRetainer(int index) const {
+ IsDeadCheck("v8::HeapSnapshot::GetRetainer");
+ return reinterpret_cast<const HeapGraphEdge*>(
+ reinterpret_cast<const i::HeapEntry*>(this)->retainers()->at(index));
+}
+
+
+int HeapGraphNode::GetRetainingPathsCount() const {
+ IsDeadCheck("v8::HeapSnapshot::GetRetainingPathsCount");
+ return const_cast<i::HeapEntry*>(
+ reinterpret_cast<const i::HeapEntry*>(
+ this))->GetRetainingPaths()->length();
+}
+
+
+const HeapGraphPath* HeapGraphNode::GetRetainingPath(int index) const {
+ IsDeadCheck("v8::HeapSnapshot::GetRetainingPath");
+ return reinterpret_cast<const HeapGraphPath*>(
+ const_cast<i::HeapEntry*>(
+ reinterpret_cast<const i::HeapEntry*>(
+ this))->GetRetainingPaths()->at(index));
+}
+
+
+unsigned HeapSnapshot::GetUid() const {
+ IsDeadCheck("v8::HeapSnapshot::GetUid");
+ return reinterpret_cast<const i::HeapSnapshot*>(this)->uid();
+}
+
+
+Handle<String> HeapSnapshot::GetTitle() const {
+ IsDeadCheck("v8::HeapSnapshot::GetTitle");
+ const i::HeapSnapshot* snapshot =
+ reinterpret_cast<const i::HeapSnapshot*>(this);
+ return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol(
+ snapshot->title())));
+}
+
+
+const HeapGraphNode* HeapSnapshot::GetHead() const {
+ IsDeadCheck("v8::HeapSnapshot::GetHead");
+ const i::HeapSnapshot* snapshot =
+ reinterpret_cast<const i::HeapSnapshot*>(this);
+ return reinterpret_cast<const HeapGraphNode*>(snapshot->const_root());
+}
+
+
+int HeapProfiler::GetSnapshotsCount() {
+ IsDeadCheck("v8::HeapProfiler::GetSnapshotsCount");
+ return i::HeapProfiler::GetSnapshotsCount();
+}
+
+
+const HeapSnapshot* HeapProfiler::GetSnapshot(int index) {
+ IsDeadCheck("v8::HeapProfiler::GetSnapshot");
+ return reinterpret_cast<const HeapSnapshot*>(
+ i::HeapProfiler::GetSnapshot(index));
+}
+
+
+const HeapSnapshot* HeapProfiler::FindSnapshot(unsigned uid) {
+ IsDeadCheck("v8::HeapProfiler::FindSnapshot");
+ return reinterpret_cast<const HeapSnapshot*>(
+ i::HeapProfiler::FindSnapshot(uid));
+}
+
+
+const HeapSnapshot* HeapProfiler::TakeSnapshot(Handle<String> title) {
+ IsDeadCheck("v8::HeapProfiler::TakeSnapshot");
+ return reinterpret_cast<const HeapSnapshot*>(
+ i::HeapProfiler::TakeSnapshot(*Utils::OpenHandle(*title)));
+}
#endif // ENABLE_LOGGING_AND_PROFILING
=======================================
--- /branches/bleeding_edge/src/heap-profiler.cc Mon Mar 8 22:38:33 2010
+++ /branches/bleeding_edge/src/heap-profiler.cc Tue Jun 15 04:44:07 2010
@@ -30,8 +30,8 @@
#include "heap-profiler.h"
#include "frames-inl.h"
#include "global-handles.h"
+#include "profile-generator.h"
#include "string-stream.h"
-#include "zone-inl.h"
namespace v8 {
namespace internal {
@@ -312,6 +312,75 @@
}
} // namespace
+
+
+HeapProfiler* HeapProfiler::singleton_ = NULL;
+
+HeapProfiler::HeapProfiler()
+ : snapshots_(new HeapSnapshotsCollection()),
+ next_snapshot_uid_(1) {
+}
+
+
+HeapProfiler::~HeapProfiler() {
+ delete snapshots_;
+}
+
+
+void HeapProfiler::Setup() {
+ if (singleton_ == NULL) {
+ singleton_ = new HeapProfiler();
+ }
+}
+
+
+void HeapProfiler::TearDown() {
+ delete singleton_;
+ singleton_ = NULL;
+}
+
+
+HeapSnapshot* HeapProfiler::TakeSnapshot(const char* name) {
+ ASSERT(singleton_ != NULL);
+ return singleton_->TakeSnapshotImpl(name);
+}
+
+
+HeapSnapshot* HeapProfiler::TakeSnapshot(String* name) {
+ ASSERT(singleton_ != NULL);
+ return singleton_->TakeSnapshotImpl(name);
+}
+
+
+HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name) {
+ HeapSnapshot* result = snapshots_->NewSnapshot(name,
next_snapshot_uid_++);
+ HeapSnapshotGenerator generator(result);
+ generator.GenerateSnapshot();
+ return result;
+}
+
+
+HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name) {
+ return TakeSnapshotImpl(snapshots_->GetName(name));
+}
+
+
+int HeapProfiler::GetSnapshotsCount() {
+ ASSERT(singleton_ != NULL);
+ return singleton_->snapshots_->snapshots()->length();
+}
+
+
+HeapSnapshot* HeapProfiler::GetSnapshot(int index) {
+ ASSERT(singleton_ != NULL);
+ return singleton_->snapshots_->snapshots()->at(index);
+}
+
+
+HeapSnapshot* HeapProfiler::FindSnapshot(unsigned uid) {
+ ASSERT(singleton_ != NULL);
+ return singleton_->snapshots_->GetSnapshot(uid);
+}
const JSObjectsClusterTreeConfig::Key JSObjectsClusterTreeConfig::kNoKey;
=======================================
--- /branches/bleeding_edge/src/heap-profiler.h Mon Mar 8 22:38:33 2010
+++ /branches/bleeding_edge/src/heap-profiler.h Tue Jun 15 04:44:07 2010
@@ -28,23 +28,46 @@
#ifndef V8_HEAP_PROFILER_H_
#define V8_HEAP_PROFILER_H_
-#include "zone.h"
+#include "zone-inl.h"
namespace v8 {
namespace internal {
#ifdef ENABLE_LOGGING_AND_PROFILING
+class HeapSnapshot;
+class HeapSnapshotsCollection;
+
// The HeapProfiler writes data to the log files, which can be
postprocessed
// to generate .hp files for use by the GHC/Valgrind tool hp2ps.
class HeapProfiler {
public:
+ static void Setup();
+ static void TearDown();
+ static HeapSnapshot* TakeSnapshot(const char* name);
+ static HeapSnapshot* TakeSnapshot(String* name);
+ static int GetSnapshotsCount();
+ static HeapSnapshot* GetSnapshot(int index);
+ static HeapSnapshot* FindSnapshot(unsigned uid);
+
+ // Obsolete interface.
// Write a single heap sample to the log file.
static void WriteSample();
private:
+ HeapProfiler();
+ ~HeapProfiler();
+ HeapSnapshot* TakeSnapshotImpl(const char* name);
+ HeapSnapshot* TakeSnapshotImpl(String* name);
+
+ // Obsolete interface.
// Update the array info with stats from obj.
static void CollectStats(HeapObject* obj, HistogramInfo* info);
+
+ HeapSnapshotsCollection* snapshots_;
+ unsigned next_snapshot_uid_;
+
+ static HeapProfiler* singleton_;
};
=======================================
--- /branches/bleeding_edge/src/profile-generator.cc Fri Jun 11 09:34:59
2010
+++ /branches/bleeding_edge/src/profile-generator.cc Tue Jun 15 04:44:07
2010
@@ -1114,7 +1114,7 @@
const char* HeapEntry::TypeAsString() {
switch (type_) {
case INTERNAL: return "/internal/";
- case JS_OBJECT: return "/object/";
+ case OBJECT: return "/object/";
case CLOSURE: return "/closure/";
case STRING: return "/string/";
case CODE: return "/code/";
@@ -1262,7 +1262,7 @@
return AddEntry(object, HeapEntry::CLOSURE,
collection_->GetName(name));
} else if (object->IsJSObject()) {
return AddEntry(object,
- HeapEntry::JS_OBJECT,
+ HeapEntry::OBJECT,
collection_->GetName(
JSObject::cast(object)->constructor_name()));
} else if (object->IsJSGlobalPropertyCell()) {
@@ -1276,10 +1276,19 @@
return AddEntry(object,
HeapEntry::STRING,
collection_->GetName(String::cast(object)));
- } else if (object->IsCode()
- || object->IsSharedFunctionInfo()
- || object->IsScript()) {
+ } else if (object->IsCode()) {
return AddEntry(object, HeapEntry::CODE);
+ } else if (object->IsSharedFunctionInfo()) {
+ SharedFunctionInfo* shared = SharedFunctionInfo::cast(object);
+ String* name = String::cast(shared->name())->length() > 0 ?
+ String::cast(shared->name()) : shared->inferred_name();
+ return AddEntry(object, HeapEntry::CODE, collection_->GetName(name));
+ } else if (object->IsScript()) {
+ Script* script = Script::cast(object);
+ return AddEntry(object,
+ HeapEntry::CODE,
+ script->name()->IsString() ?
+
collection_->GetName(String::cast(script->name())) : "");
} else if (object->IsFixedArray()) {
return AddEntry(object, HeapEntry::ARRAY);
}
=======================================
--- /branches/bleeding_edge/src/profile-generator.h Fri Jun 11 09:34:59 2010
+++ /branches/bleeding_edge/src/profile-generator.h Tue Jun 15 04:44:07 2010
@@ -429,9 +429,9 @@
class HeapGraphEdge {
public:
enum Type {
- CONTEXT_VARIABLE,
- ELEMENT,
- PROPERTY
+ CONTEXT_VARIABLE = v8::HeapGraphEdge::CONTEXT_VARIABLE,
+ ELEMENT = v8::HeapGraphEdge::ELEMENT,
+ PROPERTY = v8::HeapGraphEdge::PROPERTY
};
HeapGraphEdge(Type type, const char* name, HeapEntry* from, HeapEntry*
to);
@@ -468,12 +468,12 @@
class HeapEntry {
public:
enum Type {
- INTERNAL,
- ARRAY,
- STRING,
- JS_OBJECT,
- CODE,
- CLOSURE
+ INTERNAL = v8::HeapGraphNode::INTERNAL,
+ ARRAY = v8::HeapGraphNode::ARRAY,
+ STRING = v8::HeapGraphNode::STRING,
+ OBJECT = v8::HeapGraphNode::OBJECT,
+ CODE = v8::HeapGraphNode::CODE,
+ CLOSURE = v8::HeapGraphNode::CLOSURE
};
explicit HeapEntry(HeapSnapshot* snapshot)
=======================================
--- /branches/bleeding_edge/src/v8.cc Fri May 28 09:12:24 2010
+++ /branches/bleeding_edge/src/v8.cc Tue Jun 15 04:44:07 2010
@@ -32,6 +32,7 @@
#include "serialize.h"
#include "simulator.h"
#include "stub-cache.h"
+#include "heap-profiler.h"
#include "oprofile-agent.h"
#include "log.h"
@@ -61,6 +62,7 @@
Logger::Setup();
CpuProfiler::Setup();
+ HeapProfiler::Setup();
// Setup the platform OS support.
OS::Setup();
@@ -149,6 +151,8 @@
Top::TearDown();
+ HeapProfiler::TearDown();
+
CpuProfiler::TearDown();
Heap::TearDown();
=======================================
--- /branches/bleeding_edge/test/cctest/test-heap-profiler.cc Tue Mar 9
00:25:22 2010
+++ /branches/bleeding_edge/test/cctest/test-heap-profiler.cc Tue Jun 15
04:44:07 2010
@@ -9,6 +9,7 @@
#include "string-stream.h"
#include "cctest.h"
#include "zone-inl.h"
+#include "../include/v8-profiler.h"
namespace i = v8::internal;
using i::ClustersCoarser;
@@ -389,5 +390,164 @@
CHECK_EQ("(global property);2", printer.GetRetainers("B"));
CHECK_EQ("(global property);1", printer.GetRetainers("C"));
}
+
+
+namespace {
+
+class NamedEntriesDetector {
+ public:
+ NamedEntriesDetector()
+ : has_A1(false), has_B1(false), has_C1(false),
+ has_A2(false), has_B2(false), has_C2(false) {
+ }
+
+ void Apply(i::HeapEntry* entry) {
+ const char* node_name = entry->name();
+ if (strcmp("A1", node_name) == 0
+ && entry->GetRetainingPaths()->length() > 0) has_A1 = true;
+ if (strcmp("B1", node_name) == 0
+ && entry->GetRetainingPaths()->length() > 0) has_B1 = true;
+ if (strcmp("C1", node_name) == 0
+ && entry->GetRetainingPaths()->length() > 0) has_C1 = true;
+ if (strcmp("A2", node_name) == 0
+ && entry->GetRetainingPaths()->length() > 0) has_A2 = true;
+ if (strcmp("B2", node_name) == 0
+ && entry->GetRetainingPaths()->length() > 0) has_B2 = true;
+ if (strcmp("C2", node_name) == 0
+ && entry->GetRetainingPaths()->length() > 0) has_C2 = true;
+ }
+
+ bool has_A1;
+ bool has_B1;
+ bool has_C1;
+ bool has_A2;
+ bool has_B2;
+ bool has_C2;
+};
+
+} // namespace
+
+TEST(HeapSnapshot) {
+ v8::HandleScope scope;
+
+ v8::Handle<v8::String> token1 = v8::String::New("token1");
+ v8::Handle<v8::Context> env1 = v8::Context::New();
+ env1->SetSecurityToken(token1);
+ env1->Enter();
+
+ CompileAndRunScript(
+ "function A1() {}\n"
+ "function B1(x) { this.x = x; }\n"
+ "function C1(x) { this.x1 = x; this.x2 = x; }\n"
+ "var a1 = new A1();\n"
+ "var b1_1 = new B1(a1), b1_2 = new B1(a1);\n"
+ "var c1 = new C1(a1);");
+
+ v8::Handle<v8::String> token2 = v8::String::New("token2");
+ v8::Handle<v8::Context> env2 = v8::Context::New();
+ env2->SetSecurityToken(token2);
+ env2->Enter();
+
+ CompileAndRunScript(
+ "function A2() {}\n"
+ "function B2(x) { return function() { return typeof x; }; }\n"
+ "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n"
+ "var a2 = new A2();\n"
+ "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::String::New("env2"));
+ CHECK_EQ(1, snapshot_env2->GetHead()->GetChildrenCount());
+ const v8::HeapGraphNode* global_env2 =
+ snapshot_env2->GetHead()->GetChild(0)->GetToNode();
+
+ // Verify, that JS global object of env2 doesn't have '..1'
+ // properties, but has '..2' properties.
+ bool has_a1 = false, has_b1_1 = false, has_b1_2 = false, has_c1 = false;
+ bool has_a2 = false, has_b2_1 = false, has_b2_2 = false, has_c2 = false;
+ // This will be needed further.
+ const v8::HeapGraphNode* a2_node = NULL;
+ for (int i = 0, count = global_env2->GetChildrenCount(); i < count; ++i)
{
+ const v8::HeapGraphEdge* prop = global_env2->GetChild(i);
+ v8::String::AsciiValue prop_name(prop->GetName());
+ if (strcmp("a1", *prop_name) == 0) has_a1 = true;
+ if (strcmp("b1_1", *prop_name) == 0) has_b1_1 = true;
+ if (strcmp("b1_2", *prop_name) == 0) has_b1_2 = true;
+ if (strcmp("c1", *prop_name) == 0) has_c1 = true;
+ if (strcmp("a2", *prop_name) == 0) {
+ has_a2 = true;
+ a2_node = prop->GetToNode();
+ }
+ if (strcmp("b2_1", *prop_name) == 0) has_b2_1 = true;
+ if (strcmp("b2_2", *prop_name) == 0) has_b2_2 = true;
+ if (strcmp("c2", *prop_name) == 0) has_c2 = true;
+ }
+ CHECK(!has_a1);
+ CHECK(!has_b1_1);
+ CHECK(!has_b1_2);
+ CHECK(!has_c1);
+ CHECK(has_a2);
+ CHECK(has_b2_1);
+ CHECK(has_b2_2);
+ CHECK(has_c2);
+
+ // Verify that anything related to '[ABC]1' is not reachable.
+ NamedEntriesDetector det;
+ i::HeapSnapshot* i_snapshot_env2 =
+ const_cast<i::HeapSnapshot*>(
+ reinterpret_cast<const i::HeapSnapshot*>(snapshot_env2));
+ i_snapshot_env2->IterateEntries(&det);
+ CHECK(!det.has_A1);
+ CHECK(!det.has_B1);
+ CHECK(!det.has_C1);
+ CHECK(det.has_A2);
+ CHECK(det.has_B2);
+ CHECK(det.has_C2);
+
+ // Verify 'a2' object retainers. They are:
+ // - (global object).a2
+ // - c2.x1, c2.x2, c2[1]
+ // - b2_1 and b2_2 closures: via 'x' variable
+ CHECK_EQ(6, a2_node->GetRetainingPathsCount());
+ bool has_global_obj_a2_ref = false;
+ bool has_c2_x1_ref = false, has_c2_x2_ref = false, has_c2_1_ref = false;
+ bool has_b2_1_x_ref = false, has_b2_2_x_ref = false;
+ for (int i = 0; i < a2_node->GetRetainingPathsCount(); ++i) {
+ const v8::HeapGraphPath* path = a2_node->GetRetainingPath(i);
+ const int edges_count = path->GetEdgesCount();
+ CHECK_GT(edges_count, 0);
+ const v8::HeapGraphEdge* last_edge = path->GetEdge(edges_count - 1);
+ v8::String::AsciiValue last_edge_name(last_edge->GetName());
+ if (strcmp("a2", *last_edge_name) == 0
+ && last_edge->GetType() == v8::HeapGraphEdge::PROPERTY) {
+ has_global_obj_a2_ref = true;
+ continue;
+ }
+ CHECK_GT(edges_count, 1);
+ const v8::HeapGraphEdge* prev_edge = path->GetEdge(edges_count - 2);
+ v8::String::AsciiValue prev_edge_name(prev_edge->GetName());
+ if (strcmp("x1", *last_edge_name) == 0
+ && last_edge->GetType() == v8::HeapGraphEdge::PROPERTY
+ && strcmp("c2", *prev_edge_name) == 0) has_c2_x1_ref = true;
+ if (strcmp("x2", *last_edge_name) == 0
+ && last_edge->GetType() == v8::HeapGraphEdge::PROPERTY
+ && strcmp("c2", *prev_edge_name) == 0) has_c2_x2_ref = true;
+ if (strcmp("1", *last_edge_name) == 0
+ && last_edge->GetType() == v8::HeapGraphEdge::ELEMENT
+ && strcmp("c2", *prev_edge_name) == 0) has_c2_1_ref = true;
+ if (strcmp("x", *last_edge_name) == 0
+ && last_edge->GetType() == v8::HeapGraphEdge::CONTEXT_VARIABLE
+ && strcmp("b2_1", *prev_edge_name) == 0) has_b2_1_x_ref = true;
+ if (strcmp("x", *last_edge_name) == 0
+ && last_edge->GetType() == v8::HeapGraphEdge::CONTEXT_VARIABLE
+ && strcmp("b2_2", *prev_edge_name) == 0) has_b2_2_x_ref = true;
+ }
+ CHECK(has_global_obj_a2_ref);
+ CHECK(has_c2_x1_ref);
+ CHECK(has_c2_x2_ref);
+ CHECK(has_c2_1_ref);
+ CHECK(has_b2_1_x_ref);
+ CHECK(has_b2_2_x_ref);
+}
#endif // ENABLE_LOGGING_AND_PROFILING
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev