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

Reply via email to