Revision: 7125
Author: [email protected]
Date: Thu Mar 10 04:05:31 2011
Log: Add an interface for an embedder to provide information about native
objects retained by object groups and global handles.

This information is then used during heap snapshot generation
to provide a more complete memory picture.

This patch will be needed to fix https://bugs.webkit.org/show_bug.cgi?id=53659.

Review URL: http://codereview.chromium.org/6626043
http://code.google.com/p/v8/source/detail?r=7125

Modified:
 /branches/bleeding_edge/include/v8-profiler.h
 /branches/bleeding_edge/include/v8.h
 /branches/bleeding_edge/src/api.cc
 /branches/bleeding_edge/src/global-handles.cc
 /branches/bleeding_edge/src/global-handles.h
 /branches/bleeding_edge/src/heap-profiler.cc
 /branches/bleeding_edge/src/heap-profiler.h
 /branches/bleeding_edge/src/heap.h
 /branches/bleeding_edge/src/objects-inl.h
 /branches/bleeding_edge/src/objects.cc
 /branches/bleeding_edge/src/objects.h
 /branches/bleeding_edge/src/profile-generator.cc
 /branches/bleeding_edge/src/profile-generator.h
 /branches/bleeding_edge/test/cctest/test-heap-profiler.cc
 /branches/bleeding_edge/test/cctest/test-mark-compact.cc

=======================================
--- /branches/bleeding_edge/include/v8-profiler.h       Mon Dec 13 02:42:06 2010
+++ /branches/bleeding_edge/include/v8-profiler.h       Thu Mar 10 04:05:31 2011
@@ -245,14 +245,15 @@
 class V8EXPORT HeapGraphNode {
  public:
   enum Type {
-    kHidden = 0,     // Hidden node, may be filtered when shown to user.
-    kArray = 1,      // An array of elements.
-    kString = 2,     // A string.
-    kObject = 3,     // A JS object (except for arrays and strings).
-    kCode = 4,       // Compiled code.
-    kClosure = 5,    // Function closure.
-    kRegExp = 6,     // RegExp.
-    kHeapNumber = 7  // Number stored in the heap.
+    kHidden = 0,      // Hidden node, may be filtered when shown to user.
+    kArray = 1,       // An array of elements.
+    kString = 2,      // A string.
+    kObject = 3,      // A JS object (except for arrays and strings).
+    kCode = 4,        // Compiled code.
+    kClosure = 5,     // Function closure.
+    kRegExp = 6,      // RegExp.
+    kHeapNumber = 7,  // Number stored in the heap.
+    kNative = 8       // Native object (not from V8 heap).
   };

   /** Returns node type (see HeapGraphNode::Type). */
@@ -392,11 +393,22 @@
 };


+class RetainedObjectInfo;
+
 /**
  * Interface for controlling heap profiling.
  */
 class V8EXPORT HeapProfiler {
  public:
+  /**
+   * Callback function invoked for obtaining RetainedObjectInfo for
+   * the given JavaScript wrapper object. It is prohibited to enter V8
+   * while the callback is running: only getters on the handle and
+   * GetPointerFromInternalField on the objects are allowed.
+   */
+  typedef RetainedObjectInfo* (*WrapperInfoCallback)
+      (uint16_t class_id, Handle<Value> wrapper);
+
   /** Returns the number of snapshots taken. */
   static int GetSnapshotsCount();

@@ -414,6 +426,81 @@
       Handle<String> title,
       HeapSnapshot::Type type = HeapSnapshot::kFull,
       ActivityControl* control = NULL);
+
+  /** Binds a callback to embedder's class ID. */
+  static void DefineWrapperClass(
+      uint16_t class_id,
+      WrapperInfoCallback callback);
+
+  /**
+   * Default value of persistent handle class ID. Must not be used to
+   * define a class. Can be used to reset a class of a persistent
+   * handle.
+   */
+  static const uint16_t kPersistentHandleNoClassId = 0;
+};
+
+
+/**
+ * Interface for providing information about embedder's objects
+ * held by global handles. This information is reported in two ways:
+ *
+ *  1. When calling AddObjectGroup, an embedder may pass
+ *     RetainedObjectInfo instance describing the group.  To collect
+ *     this information while taking a heap snapshot, V8 calls GC
+ *     prologue and epilogue callbacks.
+ *
+ *  2. When a heap snapshot is collected, V8 additionally
+ *     requests RetainedObjectInfos for persistent handles that
+ *     were not previously reported via AddObjectGroup.
+ *
+ * Thus, if an embedder wants to provide information about native
+ * objects for heap snapshots, he can do it in a GC prologue
+ * handler, and / or by assigning wrapper class ids in the following way:
+ *
+ *  1. Bind a callback to class id by calling DefineWrapperClass.
+ *  2. Call SetWrapperClassId on certain persistent handles.
+ *
+ * V8 takes ownership of RetainedObjectInfo instances passed to it and
+ * keeps them alive only during snapshot collection. Afterwards, they
+ * are freed by calling the Dispose class function.
+ */
+class V8EXPORT RetainedObjectInfo {  // NOLINT
+ public:
+  /** Called by V8 when it no longer needs an instance. */
+  virtual void Dispose() = 0;
+
+  /** Returns whether two instances are equivalent. */
+  virtual bool IsEquivalent(RetainedObjectInfo* other) = 0;
+
+  /**
+   * Returns hash value for the instance. Equivalent instances
+   * must have the same hash value.
+   */
+  virtual intptr_t GetHash() = 0;
+
+  /**
+   * Returns human-readable label. It must be a NUL-terminated UTF-8
+   * encoded string. V8 copies its contents during a call to GetLabel.
+   */
+  virtual const char* GetLabel() = 0;
+
+  /**
+   * Returns element count in case if a global handle retains
+   * a subgraph by holding one of its nodes.
+   */
+  virtual intptr_t GetElementCount() { return -1; }
+
+  /** Returns embedder's object size in bytes. */
+  virtual intptr_t GetSizeInBytes() { return -1; }
+
+ protected:
+  RetainedObjectInfo() {}
+  virtual ~RetainedObjectInfo() {}
+
+ private:
+  RetainedObjectInfo(const RetainedObjectInfo&);
+  RetainedObjectInfo& operator=(const RetainedObjectInfo&);
 };


=======================================
--- /branches/bleeding_edge/include/v8.h        Wed Mar  9 07:01:16 2011
+++ /branches/bleeding_edge/include/v8.h        Thu Mar 10 04:05:31 2011
@@ -396,6 +396,12 @@
    */
   inline bool IsWeak() const;

+  /**
+   * Assigns a wrapper class ID to the handle. See RetainedObjectInfo
+   * interface description in v8-profiler.h for details.
+   */
+  inline void SetWrapperClassId(uint16_t class_id);
+
  private:
   friend class ImplementationUtilities;
   friend class ObjectTemplate;
@@ -2534,6 +2540,8 @@
 };


+class RetainedObjectInfo;
+
 /**
  * Container class for static utility functions.
  */
@@ -2703,8 +2711,11 @@
    * intended to be used in the before-garbage-collection callback
    * function, for instance to simulate DOM tree connections among JS
    * wrapper objects.
+   * See v8-profiler.h for RetainedObjectInfo interface description.
    */
-  static void AddObjectGroup(Persistent<Value>* objects, size_t length);
+  static void AddObjectGroup(Persistent<Value>* objects,
+                             size_t length,
+                             RetainedObjectInfo* info = NULL);

   /**
    * Initializes from snapshot if possible. Otherwise, attempts to
@@ -2913,6 +2924,8 @@
   static void ClearWeak(internal::Object** global_handle);
   static bool IsGlobalNearDeath(internal::Object** global_handle);
   static bool IsGlobalWeak(internal::Object** global_handle);
+  static void SetWrapperClassId(internal::Object** global_handle,
+                                uint16_t class_id);

   template <class T> friend class Handle;
   template <class T> friend class Local;
@@ -3561,6 +3574,10 @@
   V8::ClearWeak(reinterpret_cast<internal::Object**>(**this));
 }

+template <class T>
+void Persistent<T>::SetWrapperClassId(uint16_t class_id) {
+ V8::SetWrapperClassId(reinterpret_cast<internal::Object**>(**this), class_id);
+}

 Arguments::Arguments(internal::Object** implicit_args,
                      internal::Object** values, int length,
=======================================
--- /branches/bleeding_edge/src/api.cc  Wed Mar  9 07:01:16 2011
+++ /branches/bleeding_edge/src/api.cc  Thu Mar 10 04:05:31 2011
@@ -3578,6 +3578,11 @@
       i::Handle<i::Context>::cast(i::Handle<i::Object>(ctx));
i::Bootstrapper::ReattachGlobal(context, Utils::OpenHandle(*global_object));
 }
+
+
+void V8::SetWrapperClassId(i::Object** global_handle, uint16_t class_id) {
+  i::GlobalHandles::SetWrapperClassId(global_handle, class_id);
+}


 Local<v8::Object> ObjectTemplate::NewInstance() {
@@ -4116,10 +4121,13 @@
 }


-void V8::AddObjectGroup(Persistent<Value>* objects, size_t length) {
+void V8::AddObjectGroup(Persistent<Value>* objects,
+                        size_t length,
+                        RetainedObjectInfo* info) {
   if (IsDeadCheck("v8::V8::AddObjectGroup()")) return;
   STATIC_ASSERT(sizeof(Persistent<Value>) == sizeof(i::Object**));
- i::GlobalHandles::AddGroup(reinterpret_cast<i::Object***>(objects), length);
+  i::GlobalHandles::AddGroup(
+      reinterpret_cast<i::Object***>(objects), length, info);
 }


@@ -5064,6 +5072,12 @@
       i::HeapProfiler::TakeSnapshot(
           *Utils::OpenHandle(*title), internal_type, control));
 }
+
+
+void HeapProfiler::DefineWrapperClass(uint16_t class_id,
+                                      WrapperInfoCallback callback) {
+  i::HeapProfiler::DefineWrapperClass(class_id, callback);
+}

 #endif  // ENABLE_LOGGING_AND_PROFILING

=======================================
--- /branches/bleeding_edge/src/global-handles.cc       Tue Dec  7 03:31:57 2010
+++ /branches/bleeding_edge/src/global-handles.cc       Thu Mar 10 04:05:31 2011
@@ -41,6 +41,7 @@
   void Initialize(Object* object) {
     // Set the initial value of the handle.
     object_ = object;
+    class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
     state_  = NORMAL;
     parameter_or_next_free_.parameter = NULL;
     callback_ = NULL;
@@ -136,6 +137,14 @@
   bool IsWeak() {
     return state_ == WEAK;
   }
+
+  bool CanBeRetainer() {
+    return state_ != DESTROYED && state_ != NEAR_DEATH;
+  }
+
+  void SetWrapperClassId(uint16_t class_id) {
+    class_id_ = class_id;
+  }

   // Returns the id for this weak handle.
   void set_parameter(void* parameter) {
@@ -190,6 +199,8 @@
   // Place the handle address first to avoid offset computation.
   Object* object_;  // Storage for object pointer.

+  uint16_t class_id_;
+
   // Transition diagram:
// NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED }
   enum State {
@@ -199,7 +210,7 @@
     NEAR_DEATH,  // Callback has informed the handle is near death.
     DESTROYED
   };
-  State state_;
+  State state_ : 3;

  private:
   // Handle specific callback.
@@ -335,6 +346,11 @@
 bool GlobalHandles::IsWeak(Object** location) {
   return Node::FromLocation(location)->IsWeak();
 }
+
+
+void GlobalHandles::SetWrapperClassId(Object** location, uint16_t class_id) {
+  Node::FromLocation(location)->SetWrapperClassId(class_id);
+}


 void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
@@ -433,6 +449,16 @@
     }
   }
 }
+
+
+void GlobalHandles::IterateAllRootsWithClassIds(ObjectVisitor* v) {
+  for (Node* current = head_; current != NULL; current = current->next()) {
+ if (current->class_id_ != v8::HeapProfiler::kPersistentHandleNoClassId &&
+        current->CanBeRetainer()) {
+      v->VisitEmbedderReference(&current->object_, current->class_id_);
+    }
+  }
+}


 void GlobalHandles::TearDown() {
@@ -515,8 +541,10 @@
   return &groups;
 }

-void GlobalHandles::AddGroup(Object*** handles, size_t length) {
-  ObjectGroup* new_entry = new ObjectGroup(length);
+void GlobalHandles::AddGroup(Object*** handles,
+                             size_t length,
+                             v8::RetainedObjectInfo* info) {
+  ObjectGroup* new_entry = new ObjectGroup(length, info);
   for (size_t i = 0; i < length; ++i)
     new_entry->objects_.Add(handles[i]);
   ObjectGroups()->Add(new_entry);
=======================================
--- /branches/bleeding_edge/src/global-handles.h        Tue Dec  7 03:01:02 2010
+++ /branches/bleeding_edge/src/global-handles.h        Thu Mar 10 04:05:31 2011
@@ -48,10 +48,16 @@
 class ObjectGroup : public Malloced {
  public:
   ObjectGroup() : objects_(4) {}
-  explicit ObjectGroup(size_t capacity)
-      : objects_(static_cast<int>(capacity)) { }
+  ObjectGroup(size_t capacity, v8::RetainedObjectInfo* info)
+      : objects_(static_cast<int>(capacity)),
+        info_(info) { }
+  ~ObjectGroup() { if (info_ != NULL) info_->Dispose(); }

   List<Object**> objects_;
+  v8::RetainedObjectInfo* info_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ObjectGroup);
 };


@@ -75,6 +81,8 @@
                        void* parameter,
                        WeakReferenceCallback callback);

+  static void SetWrapperClassId(Object** location, uint16_t class_id);
+
   // Returns the current number of weak handles.
   static int NumberOfWeakHandles() { return number_of_weak_handles_; }

@@ -105,6 +113,9 @@
   // Iterates over all handles.
   static void IterateAllRoots(ObjectVisitor* v);

+  // Iterates over all handles that have embedder-assigned class ID.
+  static void IterateAllRootsWithClassIds(ObjectVisitor* v);
+
   // Iterates over all weak roots in heap.
   static void IterateWeakRoots(ObjectVisitor* v);

@@ -119,7 +130,9 @@
   // Add an object group.
   // Should only used in GC callback function before a collection.
   // All groups are destroyed after a mark-compact collection.
-  static void AddGroup(Object*** handles, size_t length);
+  static void AddGroup(Object*** handles,
+                       size_t length,
+                       v8::RetainedObjectInfo* info);

   // Returns the object groups.
   static List<ObjectGroup*>* ObjectGroups();
=======================================
--- /branches/bleeding_edge/src/heap-profiler.cc        Tue Mar  1 09:38:49 2011
+++ /branches/bleeding_edge/src/heap-profiler.cc        Thu Mar 10 04:05:31 2011
@@ -362,6 +362,26 @@
   ASSERT(singleton_ != NULL);
   return singleton_->TakeSnapshotImpl(name, type, control);
 }
+
+
+void HeapProfiler::DefineWrapperClass(
+    uint16_t class_id, v8::HeapProfiler::WrapperInfoCallback callback) {
+  ASSERT(singleton_ != NULL);
+  if (singleton_->wrapper_callbacks_.length() <= class_id) {
+    singleton_->wrapper_callbacks_.AddBlock(
+        NULL, class_id - singleton_->wrapper_callbacks_.length() + 1);
+  }
+  singleton_->wrapper_callbacks_[class_id] = callback;
+}
+
+
+v8::RetainedObjectInfo* HeapProfiler::ExecuteWrapperClassCallback(
+    uint16_t class_id, Object** wrapper) {
+  ASSERT(singleton_ != NULL);
+  if (singleton_->wrapper_callbacks_.length() <= class_id) return NULL;
+  return singleton_->wrapper_callbacks_[class_id](
+      class_id, Utils::ToLocal(Handle<Object>(wrapper)));
+}


 HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name,
@@ -401,7 +421,7 @@
 HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name,
                                              int type,
v8::ActivityControl* control) {
-  return TakeSnapshotImpl(snapshots_->GetName(name), type, control);
+ return TakeSnapshotImpl(snapshots_->names()->GetName(name), type, control);
 }


@@ -872,7 +892,8 @@
             const NumberAndSizeInfo& number_and_size) {
     const char* name = cluster.GetSpecialCaseName();
     if (name == NULL) {
- name = snapshot_->collection()->GetFunctionName(cluster.constructor());
+      name = snapshot_->collection()->names()->GetFunctionName(
+          cluster.constructor());
     }
     AddEntryFromAggregatedSnapshot(snapshot_,
                                    root_child_index_,
@@ -1013,7 +1034,8 @@
     JSObjectsCluster cluster = HeapObjectAsCluster(obj);
     const char* name = cluster.GetSpecialCaseName();
     if (name == NULL) {
- name = snapshot_->collection()->GetFunctionName(cluster.constructor());
+      name = snapshot_->collection()->names()->GetFunctionName(
+          cluster.constructor());
     }
     return AddEntryFromAggregatedSnapshot(
         snapshot_, root_child_index_, HeapEntry::kObject, name,
=======================================
--- /branches/bleeding_edge/src/heap-profiler.h Tue Mar  1 09:38:49 2011
+++ /branches/bleeding_edge/src/heap-profiler.h Thu Mar 10 04:05:31 2011
@@ -68,6 +68,11 @@

   static void ObjectMoveEvent(Address from, Address to);

+  static void DefineWrapperClass(
+      uint16_t class_id, v8::HeapProfiler::WrapperInfoCallback callback);
+ static v8::RetainedObjectInfo* ExecuteWrapperClassCallback(uint16_t class_id, + Object** wrapper);
+
   static INLINE(bool is_profiling()) {
return singleton_ != NULL && singleton_->snapshots_->is_tracking_objects();
   }
@@ -88,6 +93,7 @@

   HeapSnapshotsCollection* snapshots_;
   unsigned next_snapshot_uid_;
+  List<v8::HeapProfiler::WrapperInfoCallback> wrapper_callbacks_;

   static HeapProfiler* singleton_;
 #endif  // ENABLE_LOGGING_AND_PROFILING
=======================================
--- /branches/bleeding_edge/src/heap.h  Wed Mar  9 07:01:16 2011
+++ /branches/bleeding_edge/src/heap.h  Thu Mar 10 04:05:31 2011
@@ -1137,6 +1137,14 @@
   static void ClearNormalizedMapCaches();

   static GCTracer* tracer() { return tracer_; }
+
+  static void CallGlobalGCPrologueCallback() {
+ if (global_gc_prologue_callback_ != NULL) global_gc_prologue_callback_();
+  }
+
+  static void CallGlobalGCEpilogueCallback() {
+ if (global_gc_epilogue_callback_ != NULL) global_gc_epilogue_callback_();
+  }

  private:
   static int reserved_semispace_size_;
=======================================
--- /branches/bleeding_edge/src/objects-inl.h   Wed Mar  9 07:01:16 2011
+++ /branches/bleeding_edge/src/objects-inl.h   Thu Mar 10 04:05:31 2011
@@ -3666,6 +3666,22 @@
   }
   return result;
 }
+
+
+template <typename schar>
+uint32_t HashSequentialString(const schar* chars, int length) {
+  StringHasher hasher(length);
+  if (!hasher.has_trivial_hash()) {
+    int i;
+    for (i = 0; hasher.is_array_index() && (i < length); i++) {
+      hasher.AddCharacter(chars[i]);
+    }
+    for (; i < length; i++) {
+      hasher.AddCharacterNoIndex(chars[i]);
+    }
+  }
+  return hasher.GetHashField();
+}


 bool String::AsArrayIndex(uint32_t* index) {
=======================================
--- /branches/bleeding_edge/src/objects.cc      Wed Mar  9 07:01:16 2011
+++ /branches/bleeding_edge/src/objects.cc      Thu Mar 10 04:05:31 2011
@@ -5236,22 +5236,6 @@
   }
   return true;
 }
-
-
-template <typename schar>
-static inline uint32_t HashSequentialString(const schar* chars, int length) {
-  StringHasher hasher(length);
-  if (!hasher.has_trivial_hash()) {
-    int i;
-    for (i = 0; hasher.is_array_index() && (i < length); i++) {
-      hasher.AddCharacter(chars[i]);
-    }
-    for (; i < length; i++) {
-      hasher.AddCharacterNoIndex(chars[i]);
-    }
-  }
-  return hasher.GetHashField();
-}


 uint32_t String::ComputeAndSetHash() {
=======================================
--- /branches/bleeding_edge/src/objects.h       Wed Mar  9 07:01:16 2011
+++ /branches/bleeding_edge/src/objects.h       Thu Mar 10 04:05:31 2011
@@ -5154,6 +5154,11 @@
 };


+// Calculates string hash.
+template <typename schar>
+inline uint32_t HashSequentialString(const schar* chars, int length);
+
+
 // The characteristics of a string are stored in its map.  Retrieving these
 // few bits of information is moderately expensive, involving two memory
 // loads where the second is dependent on the first.  To improve efficiency
@@ -6534,6 +6539,9 @@
   inline void VisitExternalReference(Address* p) {
     VisitExternalReferences(p, p + 1);
   }
+
+  // Visits a handle that has an embedder-assigned class ID.
+  virtual void VisitEmbedderReference(Object** p, uint16_t class_id) {}

 #ifdef DEBUG
   // Intended for serialization/deserialization checking: insert, or
=======================================
--- /branches/bleeding_edge/src/profile-generator.cc Thu Mar 10 04:00:27 2011 +++ /branches/bleeding_edge/src/profile-generator.cc Thu Mar 10 04:05:31 2011
@@ -29,6 +29,7 @@

 #include "v8.h"
 #include "global-handles.h"
+#include "heap-profiler.h"
 #include "scopeinfo.h"
 #include "top.h"
 #include "unicode.h"
@@ -92,11 +93,6 @@
 StringsStorage::StringsStorage()
     : names_(StringsMatch) {
 }
-
-
-static void DeleteIndexName(char** name_ptr) {
-  DeleteArray(*name_ptr);
-}


 StringsStorage::~StringsStorage() {
@@ -105,40 +101,64 @@
        p = names_.Next(p)) {
     DeleteArray(reinterpret_cast<const char*>(p->value));
   }
-  index_names_.Iterate(DeleteIndexName);
+}
+
+
+const char* StringsStorage::GetCopy(const char* src) {
+  int len = strlen(src);
+  Vector<char> dst = Vector<char>::New(len + 1);
+  OS::StrNCpy(dst, src, len);
+  dst[len] = '\0';
+  uint32_t hash = HashSequentialString(dst.start(), len);
+  return AddOrDisposeString(dst.start(), hash);
+}
+
+
+const char* StringsStorage::GetFormatted(const char* format, ...) {
+  va_list args;
+  va_start(args, format);
+  const char* result = GetVFormatted(format, args);
+  va_end(args);
+  return result;
+}
+
+
+const char* StringsStorage::AddOrDisposeString(char* str, uint32_t hash) {
+  HashMap::Entry* cache_entry = names_.Lookup(str, hash, true);
+  if (cache_entry->value == NULL) {
+    // New entry added.
+    cache_entry->value = str;
+  } else {
+    DeleteArray(str);
+  }
+  return reinterpret_cast<const char*>(cache_entry->value);
+}
+
+
+const char* StringsStorage::GetVFormatted(const char* format, va_list args) {
+  Vector<char> str = Vector<char>::New(1024);
+  int len = OS::VSNPrintF(str, format, args);
+  if (len == -1) {
+    DeleteArray(str.start());
+    return format;
+  }
+  uint32_t hash = HashSequentialString(str.start(), len);
+  return AddOrDisposeString(str.start(), hash);
 }


 const char* StringsStorage::GetName(String* name) {
   if (name->IsString()) {
-    char* c_name =
-        name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).Detach();
- HashMap::Entry* cache_entry = names_.Lookup(c_name, name->Hash(), true);
-    if (cache_entry->value == NULL) {
-      // New entry added.
-      cache_entry->value = c_name;
-    } else {
-      DeleteArray(c_name);
-    }
-    return reinterpret_cast<const char*>(cache_entry->value);
+    return AddOrDisposeString(
+        name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).Detach(),
+        name->Hash());
   }
   return "";
 }


 const char* StringsStorage::GetName(int index) {
-  ASSERT(index >= 0);
-  if (index_names_.length() <= index) {
-    index_names_.AddBlock(
-        NULL, index - index_names_.length() + 1);
-  }
-  if (index_names_[index] == NULL) {
-    const int kMaximumNameLength = 32;
-    char* name = NewArray<char>(kMaximumNameLength);
-    OS::SNPrintF(Vector<char>(name, kMaximumNameLength), "%d", index);
-    index_names_[index] = name;
-  }
-  return index_names_[index];
+  return GetFormatted("%d", index);
 }


@@ -1009,6 +1029,7 @@
     case kArray: return "/array/";
     case kRegExp: return "/regexp/";
     case kHeapNumber: return "/number/";
+    case kNative: return "/native/";
     default: return "???";
   }
 }
@@ -1205,6 +1226,7 @@
       uid_(uid),
       root_entry_(NULL),
       gc_roots_entry_(NULL),
+      dom_subtrees_root_entry_(NULL),
       raw_entries_(NULL),
       entries_sorted_(false),
       retaining_paths_(HeapEntry::Match) {
@@ -1277,6 +1299,19 @@
                                      children_count,
                                      retainers_count));
 }
+
+
+HeapEntry* HeapSnapshot::AddNativesRootEntry(int children_count,
+                                                 int retainers_count) {
+  ASSERT(dom_subtrees_root_entry_ == NULL);
+  return (dom_subtrees_root_entry_ = AddEntry(
+      HeapEntry::kObject,
+      "(Native objects)",
+      HeapObjectsMap::kNativesRootObjectId,
+      0,
+      children_count,
+      retainers_count));
+}


 HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
@@ -1372,10 +1407,13 @@
 }


-const uint64_t HeapObjectsMap::kInternalRootObjectId = 0;
-const uint64_t HeapObjectsMap::kGcRootsObjectId = 1;
+// We split IDs on evens for embedder objects (see
+// HeapObjectsMap::GenerateId) and odds for native objects.
+const uint64_t HeapObjectsMap::kInternalRootObjectId = 1;
+const uint64_t HeapObjectsMap::kGcRootsObjectId = 3;
+const uint64_t HeapObjectsMap::kNativesRootObjectId = 5;
 // Increase kFirstAvailableObjectId if new 'special' objects appear.
-const uint64_t HeapObjectsMap::kFirstAvailableObjectId = 2;
+const uint64_t HeapObjectsMap::kFirstAvailableObjectId = 7;

 HeapObjectsMap::HeapObjectsMap()
     : initial_fill_mode_(true),
@@ -1400,7 +1438,8 @@
     uint64_t existing = FindEntry(addr);
     if (existing != 0) return existing;
   }
-  uint64_t id = next_id_++;
+  uint64_t id = next_id_;
+  next_id_ += 2;
   AddEntry(addr, id);
   return id;
 }
@@ -1466,6 +1505,17 @@
   delete entries_;
   entries_ = new_entries;
 }
+
+
+uint64_t HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) {
+  uint64_t id = static_cast<uint64_t>(info->GetHash());
+  const char* label = info->GetLabel();
+  id ^= HashSequentialString(label, strlen(label));
+  intptr_t element_count = info->GetElementCount();
+  if (element_count != -1)
+    id ^= ComputeIntegerHash(static_cast<uint32_t>(element_count));
+  return id << 1;
+}


 HeapSnapshotsCollection::HeapSnapshotsCollection()
@@ -1551,6 +1601,8 @@
         p->key,
         entry_info->children_count,
         entry_info->retainers_count);
+    ASSERT(entry_info->entry != NULL);
+    ASSERT(entry_info->entry != kHeapEntryPlaceholder);
     entry_info->children_count = 0;
     entry_info->retainers_count = 0;
   }
@@ -1630,9 +1682,11 @@


 HeapObject *const V8HeapExplorer::kInternalRootObject =
-    reinterpret_cast<HeapObject*>(1);
+    reinterpret_cast<HeapObject*>(
+        static_cast<intptr_t>(HeapObjectsMap::kInternalRootObjectId));
 HeapObject *const V8HeapExplorer::kGcRootsObject =
-    reinterpret_cast<HeapObject*>(2);
+    reinterpret_cast<HeapObject*>(
+        static_cast<intptr_t>(HeapObjectsMap::kGcRootsObjectId));


 V8HeapExplorer::V8HeapExplorer(
@@ -1669,27 +1723,28 @@
     SharedFunctionInfo* shared = func->shared();
     return AddEntry(object,
                     HeapEntry::kClosure,
-                    collection_->GetName(String::cast(shared->name())),
+ collection_->names()->GetName(String::cast(shared->name())),
                     children_count,
                     retainers_count);
   } else if (object->IsJSRegExp()) {
     JSRegExp* re = JSRegExp::cast(object);
     return AddEntry(object,
                     HeapEntry::kRegExp,
-                    collection_->GetName(re->Pattern()),
+                    collection_->names()->GetName(re->Pattern()),
                     children_count,
                     retainers_count);
   } else if (object->IsJSObject()) {
     return AddEntry(object,
                     HeapEntry::kObject,
-                    collection_->GetName(GetConstructorNameForHeapProfile(
-                        JSObject::cast(object))),
+                    collection_->names()->GetName(
+                        GetConstructorNameForHeapProfile(
+                            JSObject::cast(object))),
                     children_count,
                     retainers_count);
   } else if (object->IsString()) {
     return AddEntry(object,
                     HeapEntry::kString,
-                    collection_->GetName(String::cast(object)),
+                    collection_->names()->GetName(String::cast(object)),
                     children_count,
                     retainers_count);
   } else if (object->IsCode()) {
@@ -1702,7 +1757,7 @@
     SharedFunctionInfo* shared = SharedFunctionInfo::cast(object);
     return AddEntry(object,
                     HeapEntry::kCode,
-                    collection_->GetName(String::cast(shared->name())),
+ collection_->names()->GetName(String::cast(shared->name())),
                     children_count,
                     retainers_count);
   } else if (object->IsScript()) {
@@ -1710,7 +1765,9 @@
     return AddEntry(object,
                     HeapEntry::kCode,
                     script->name()->IsString() ?
- collection_->GetName(String::cast(script->name())) : "",
+                        collection_->names()->GetName(
+                            String::cast(script->name()))
+                        : "",
                     children_count,
                     retainers_count);
   } else if (object->IsFixedArray()) {
@@ -1749,8 +1806,8 @@


 void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) {
-  filler->AddEntry(kInternalRootObject);
-  filler->AddEntry(kGcRootsObject);
+  filler->AddEntry(kInternalRootObject, this);
+  filler->AddEntry(kGcRootsObject, this);
 }


@@ -1940,7 +1997,7 @@

 HeapEntry* V8HeapExplorer::GetEntry(Object* obj) {
   if (!obj->IsHeapObject()) return NULL;
-  return filler_->FindOrAddEntry(obj);
+  return filler_->FindOrAddEntry(obj, this);
 }


@@ -1992,7 +2049,7 @@
     filler_->SetNamedReference(HeapGraphEdge::kContextVariable,
                                parent_obj,
                                parent_entry,
-                               collection_->GetName(reference_name),
+ collection_->names()->GetName(reference_name),
                                child_obj,
                                child_entry);
     known_references_.Insert(child_obj);
@@ -2043,7 +2100,7 @@
     filler_->SetNamedReference(HeapGraphEdge::kInternal,
                                parent_obj,
                                parent_entry,
-                               collection_->GetName(index),
+                               collection_->names()->GetName(index),
                                child_obj,
                                child_entry);
     known_references_.Insert(child_obj);
@@ -2078,7 +2135,7 @@
     filler_->SetNamedReference(type,
                                parent_obj,
                                parent_entry,
-                               collection_->GetName(reference_name),
+ collection_->names()->GetName(reference_name),
                                child_obj,
                                child_entry);
     known_references_.Insert(child_obj);
@@ -2096,7 +2153,7 @@
     filler_->SetNamedReference(HeapGraphEdge::kShortcut,
                                parent_obj,
                                parent_entry,
-                               collection_->GetName(reference_name),
+ collection_->names()->GetName(reference_name),
                                child_obj,
                                child_entry);
   }
@@ -2130,27 +2187,217 @@
         child_obj, child_entry);
   }
 }
+
+
+class GlobalHandlesExtractor : public ObjectVisitor {
+ public:
+  explicit GlobalHandlesExtractor(NativeObjectsExplorer* explorer)
+      : explorer_(explorer) {}
+  virtual ~GlobalHandlesExtractor() {}
+  virtual void VisitPointers(Object** start, Object** end) {
+    UNREACHABLE();
+  }
+  virtual void VisitEmbedderReference(Object** p, uint16_t class_id) {
+    explorer_->VisitSubtreeWrapper(p, class_id);
+  }
+ private:
+  NativeObjectsExplorer* explorer_;
+};
+
+HeapThing const NativeObjectsExplorer::kNativesRootObject =
+    reinterpret_cast<HeapThing>(
+        static_cast<intptr_t>(HeapObjectsMap::kNativesRootObjectId));
+
+
+NativeObjectsExplorer::NativeObjectsExplorer(
+ HeapSnapshot* snapshot, SnapshottingProgressReportingInterface* progress)
+    : snapshot_(snapshot),
+      collection_(snapshot_->collection()),
+      progress_(progress),
+      embedder_queried_(false),
+      objects_by_info_(RetainedInfosMatch),
+      filler_(NULL) {
+}
+
+
+NativeObjectsExplorer::~NativeObjectsExplorer() {
+  for (HashMap::Entry* p = objects_by_info_.Start();
+       p != NULL;
+       p = objects_by_info_.Next(p)) {
+    v8::RetainedObjectInfo* info =
+        reinterpret_cast<v8::RetainedObjectInfo*>(p->key);
+    info->Dispose();
+    List<HeapObject*>* objects =
+        reinterpret_cast<List<HeapObject*>* >(p->value);
+    delete objects;
+  }
+}
+
+
+HeapEntry* NativeObjectsExplorer::AllocateEntry(
+    HeapThing ptr, int children_count, int retainers_count) {
+  if (ptr == kNativesRootObject) {
+    return snapshot_->AddNativesRootEntry(children_count, retainers_count);
+  } else {
+    v8::RetainedObjectInfo* info =
+        reinterpret_cast<v8::RetainedObjectInfo*>(ptr);
+    intptr_t elements = info->GetElementCount();
+    intptr_t size = info->GetSizeInBytes();
+    return snapshot_->AddEntry(
+        HeapEntry::kNative,
+        elements != -1 ?
+            collection_->names()->GetFormatted(
+                "%s / %" V8_PTR_PREFIX "d entries",
+                info->GetLabel(),
+                info->GetElementCount()) :
+                collection_->names()->GetCopy(info->GetLabel()),
+        HeapObjectsMap::GenerateId(info),
+        size != -1 ? static_cast<int>(size) : 0,
+        children_count,
+        retainers_count);
+  }
+}
+
+
+void NativeObjectsExplorer::AddRootEntries(SnapshotFillerInterface* filler) {
+  if (EstimateObjectsCount() <= 0) return;
+  filler->AddEntry(kNativesRootObject, this);
+}
+
+
+int NativeObjectsExplorer::EstimateObjectsCount() {
+  FillRetainedObjects();
+  return objects_by_info_.occupancy();
+}
+
+
+void NativeObjectsExplorer::FillRetainedObjects() {
+  if (embedder_queried_) return;
+  // Record objects that are joined into ObjectGroups.
+  Heap::CallGlobalGCPrologueCallback();
+  List<ObjectGroup*>* groups = GlobalHandles::ObjectGroups();
+  for (int i = 0; i < groups->length(); ++i) {
+    ObjectGroup* group = groups->at(i);
+    if (group->info_ == NULL) continue;
+    List<HeapObject*>* list = GetListMaybeDisposeInfo(group->info_);
+    for (int j = 0; j < group->objects_.length(); ++j) {
+      HeapObject* obj = HeapObject::cast(*group->objects_[j]);
+      list->Add(obj);
+      in_groups_.Insert(obj);
+    }
+    group->info_ = NULL;  // Acquire info object ownership.
+  }
+  GlobalHandles::RemoveObjectGroups();
+  Heap::CallGlobalGCEpilogueCallback();
+  // Record objects that are not in ObjectGroups, but have class ID.
+  GlobalHandlesExtractor extractor(this);
+  GlobalHandles::IterateAllRootsWithClassIds(&extractor);
+  embedder_queried_ = true;
+}
+
+
+List<HeapObject*>* NativeObjectsExplorer::GetListMaybeDisposeInfo(
+    v8::RetainedObjectInfo* info) {
+  HashMap::Entry* entry =
+      objects_by_info_.Lookup(info, InfoHash(info), true);
+  if (entry->value != NULL) {
+    info->Dispose();
+  } else {
+    entry->value = new List<HeapObject*>(4);
+  }
+  return reinterpret_cast<List<HeapObject*>* >(entry->value);
+}
+
+
+bool NativeObjectsExplorer::IterateAndExtractReferences(
+    SnapshotFillerInterface* filler) {
+  if (EstimateObjectsCount() <= 0) return true;
+  filler_ = filler;
+  FillRetainedObjects();
+  for (HashMap::Entry* p = objects_by_info_.Start();
+       p != NULL;
+       p = objects_by_info_.Next(p)) {
+    v8::RetainedObjectInfo* info =
+        reinterpret_cast<v8::RetainedObjectInfo*>(p->key);
+    SetNativeRootReference(info);
+    List<HeapObject*>* objects =
+        reinterpret_cast<List<HeapObject*>* >(p->value);
+    for (int i = 0; i < objects->length(); ++i) {
+      SetWrapperNativeReferences(objects->at(i), info);
+    }
+  }
+  SetRootNativesRootReference();
+  filler_ = NULL;
+  return true;
+}
+
+
+void NativeObjectsExplorer::SetNativeRootReference(
+    v8::RetainedObjectInfo* info) {
+  HeapEntry* child_entry = filler_->FindOrAddEntry(info, this);
+  ASSERT(child_entry != NULL);
+  filler_->SetIndexedAutoIndexReference(
+      HeapGraphEdge::kElement,
+      kNativesRootObject, snapshot_->dom_subtrees_root(),
+      info, child_entry);
+}
+
+
+void NativeObjectsExplorer::SetWrapperNativeReferences(
+    HeapObject* wrapper, v8::RetainedObjectInfo* info) {
+  HeapEntry* wrapper_entry = filler_->FindEntry(wrapper);
+  ASSERT(wrapper_entry != NULL);
+  HeapEntry* info_entry = filler_->FindOrAddEntry(info, this);
+  ASSERT(info_entry != NULL);
+  filler_->SetNamedReference(HeapGraphEdge::kInternal,
+                             wrapper, wrapper_entry,
+                             "Native",
+                             info, info_entry);
+  filler_->SetIndexedAutoIndexReference(HeapGraphEdge::kElement,
+                                        info, info_entry,
+                                        wrapper, wrapper_entry);
+}
+
+
+void NativeObjectsExplorer::SetRootNativesRootReference() {
+  filler_->SetIndexedAutoIndexReference(
+      HeapGraphEdge::kElement,
+      V8HeapExplorer::kInternalRootObject, snapshot_->root(),
+      kNativesRootObject, snapshot_->dom_subtrees_root());
+}
+
+
+void NativeObjectsExplorer::VisitSubtreeWrapper(Object** p, uint16_t class_id) {
+  if (in_groups_.Contains(*p)) return;
+  v8::RetainedObjectInfo* info =
+      HeapProfiler::ExecuteWrapperClassCallback(class_id, p);
+  if (info == NULL) return;
+  GetListMaybeDisposeInfo(info)->Add(HeapObject::cast(*p));
+}


 HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot,
                                              v8::ActivityControl* control)
     : snapshot_(snapshot),
       control_(control),
-      v8_heap_explorer_(snapshot_, this) {
+      v8_heap_explorer_(snapshot_, this),
+      dom_explorer_(snapshot_, this) {
 }


 class SnapshotCounter : public SnapshotFillerInterface {
  public:
-  SnapshotCounter(HeapEntriesAllocator* allocator, HeapEntriesMap* entries)
-      : allocator_(allocator), entries_(entries) { }
-  HeapEntry* AddEntry(HeapThing ptr) {
-    entries_->Pair(ptr, allocator_, HeapEntriesMap::kHeapEntryPlaceholder);
+  explicit SnapshotCounter(HeapEntriesMap* entries) : entries_(entries) { }
+  HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
+    entries_->Pair(ptr, allocator, HeapEntriesMap::kHeapEntryPlaceholder);
     return HeapEntriesMap::kHeapEntryPlaceholder;
   }
-  HeapEntry* FindOrAddEntry(HeapThing ptr) {
-    HeapEntry* entry = entries_->Map(ptr);
-    return entry != NULL ? entry : AddEntry(ptr);
+  HeapEntry* FindEntry(HeapThing ptr) {
+    return entries_->Map(ptr);
+  }
+ HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
+    HeapEntry* entry = FindEntry(ptr);
+    return entry != NULL ? entry : AddEntry(ptr, allocator);
   }
   void SetIndexedReference(HeapGraphEdge::Type,
                            HeapThing parent_ptr,
@@ -2183,7 +2430,6 @@
     entries_->CountReference(parent_ptr, child_ptr);
   }
  private:
-  HeapEntriesAllocator* allocator_;
   HeapEntriesMap* entries_;
 };

@@ -2194,13 +2440,16 @@
       : snapshot_(snapshot),
         collection_(snapshot->collection()),
         entries_(entries) { }
-  HeapEntry* AddEntry(HeapThing ptr) {
+  HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
     UNREACHABLE();
     return NULL;
   }
-  HeapEntry* FindOrAddEntry(HeapThing ptr) {
-    HeapEntry* entry = entries_->Map(ptr);
-    return entry != NULL ? entry : AddEntry(ptr);
+  HeapEntry* FindEntry(HeapThing ptr) {
+    return entries_->Map(ptr);
+  }
+ HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
+    HeapEntry* entry = FindEntry(ptr);
+    return entry != NULL ? entry : AddEntry(ptr, allocator);
   }
   void SetIndexedReference(HeapGraphEdge::Type type,
                            HeapThing parent_ptr,
@@ -2247,7 +2496,7 @@
         parent_ptr, child_ptr, &child_index, &retainer_index);
     parent_entry->SetNamedReference(type,
                               child_index,
-                              collection_->GetName(child_index + 1),
+ collection_->names()->GetName(child_index + 1),
                               child_entry,
                               retainer_index);
   }
@@ -2303,21 +2552,28 @@

 void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) {
   if (control_ == NULL) return;
- progress_total_ = v8_heap_explorer_.EstimateObjectsCount() * iterations_count;
+  progress_total_ = (
+      v8_heap_explorer_.EstimateObjectsCount() +
+      dom_explorer_.EstimateObjectsCount()) * iterations_count;
   progress_counter_ = 0;
 }


 bool HeapSnapshotGenerator::CountEntriesAndReferences() {
-  SnapshotCounter counter(&v8_heap_explorer_, &entries_);
+  SnapshotCounter counter(&entries_);
   v8_heap_explorer_.AddRootEntries(&counter);
-  return v8_heap_explorer_.IterateAndExtractReferences(&counter);
+  dom_explorer_.AddRootEntries(&counter);
+  return
+      v8_heap_explorer_.IterateAndExtractReferences(&counter) &&
+      dom_explorer_.IterateAndExtractReferences(&counter);
 }


 bool HeapSnapshotGenerator::FillReferences() {
   SnapshotFiller filler(snapshot_, &entries_);
-  return v8_heap_explorer_.IterateAndExtractReferences(&filler);
+  return
+      v8_heap_explorer_.IterateAndExtractReferences(&filler) &&
+      dom_explorer_.IterateAndExtractReferences(&filler);
 }


@@ -2735,7 +2991,8 @@
             "," JSON_S("code")
             "," JSON_S("closure")
             "," JSON_S("regexp")
-            "," JSON_S("number"))
+            "," JSON_S("number")
+            "," JSON_S("native"))
         "," JSON_S("string")
         "," JSON_S("number")
         "," JSON_S("number")
=======================================
--- /branches/bleeding_edge/src/profile-generator.h     Thu Mar 10 04:00:27 2011
+++ /branches/bleeding_edge/src/profile-generator.h     Thu Mar 10 04:05:31 2011
@@ -66,6 +66,9 @@
   StringsStorage();
   ~StringsStorage();

+  const char* GetCopy(const char* src);
+  const char* GetFormatted(const char* format, ...);
+  const char* GetVFormatted(const char* format, va_list args);
   const char* GetName(String* name);
   const char* GetName(int index);
   inline const char* GetFunctionName(String* name);
@@ -76,11 +79,10 @@
     return strcmp(reinterpret_cast<char*>(key1),
                   reinterpret_cast<char*>(key2)) == 0;
   }
+  const char* AddOrDisposeString(char* str, uint32_t hash);

   // Mapping of strings by String::Hash to const char* strings.
   HashMap names_;
-  // Mapping from ints to char* strings.
-  List<char*> index_names_;

   DISALLOW_COPY_AND_ASSIGN(StringsStorage);
 };
@@ -517,7 +519,8 @@
     kCode = v8::HeapGraphNode::kCode,
     kClosure = v8::HeapGraphNode::kClosure,
     kRegExp = v8::HeapGraphNode::kRegExp,
-    kHeapNumber = v8::HeapGraphNode::kHeapNumber
+    kHeapNumber = v8::HeapGraphNode::kHeapNumber,
+    kNative = v8::HeapGraphNode::kNative
   };

   HeapEntry() { }
@@ -604,8 +607,8 @@
   const char* TypeAsString();

   unsigned painted_: 2;
-  unsigned type_: 3;
-  int children_count_: 27;
+  unsigned type_: 4;
+  int children_count_: 26;
   int retainers_count_;
   int self_size_;
   union {
@@ -677,6 +680,7 @@
   unsigned uid() { return uid_; }
   HeapEntry* root() { return root_entry_; }
   HeapEntry* gc_roots() { return gc_roots_entry_; }
+  HeapEntry* dom_subtrees_root() { return dom_subtrees_root_entry_; }
   List<HeapEntry*>* entries() { return &entries_; }

   void AllocateEntries(
@@ -689,6 +693,7 @@
                       int retainers_count);
   HeapEntry* AddRootEntry(int children_count);
   HeapEntry* AddGcRootsEntry(int children_count, int retainers_count);
+  HeapEntry* AddNativesRootEntry(int children_count, int retainers_count);
   void ClearPaint();
   HeapSnapshotsDiff* CompareWith(HeapSnapshot* snapshot);
   HeapEntry* GetEntryById(uint64_t id);
@@ -710,6 +715,7 @@
   unsigned uid_;
   HeapEntry* root_entry_;
   HeapEntry* gc_roots_entry_;
+  HeapEntry* dom_subtrees_root_entry_;
   char* raw_entries_;
   List<HeapEntry*> entries_;
   bool entries_sorted_;
@@ -733,8 +739,11 @@
   uint64_t FindObject(Address addr);
   void MoveObject(Address from, Address to);

+  static uint64_t GenerateId(v8::RetainedObjectInfo* info);
+
   static const uint64_t kInternalRootObjectId;
   static const uint64_t kGcRootsObjectId;
+  static const uint64_t kNativesRootObjectId;
   static const uint64_t kFirstAvailableObjectId;

  private:
@@ -832,12 +841,7 @@
   List<HeapSnapshot*>* snapshots() { return &snapshots_; }
   HeapSnapshot* GetSnapshot(unsigned uid);

-  const char* GetName(String* name) { return names_.GetName(name); }
-  const char* GetName(int index) { return names_.GetName(index); }
-  const char* GetFunctionName(String* name) {
-    return names_.GetFunctionName(name);
-  }
-
+  StringsStorage* names() { return &names_; }
   TokenEnumerator* token_enumerator() { return token_enumerator_; }

   uint64_t GetObjectId(Address addr) { return ids_.FindObject(addr); }
@@ -950,8 +954,11 @@
 class SnapshotFillerInterface {
  public:
   virtual ~SnapshotFillerInterface() { }
-  virtual HeapEntry* AddEntry(HeapThing ptr) = 0;
-  virtual HeapEntry* FindOrAddEntry(HeapThing ptr) = 0;
+  virtual HeapEntry* AddEntry(HeapThing ptr,
+                              HeapEntriesAllocator* allocator) = 0;
+  virtual HeapEntry* FindEntry(HeapThing ptr) = 0;
+  virtual HeapEntry* FindOrAddEntry(HeapThing ptr,
+                                    HeapEntriesAllocator* allocator) = 0;
   virtual void SetIndexedReference(HeapGraphEdge::Type type,
                                    HeapThing parent_ptr,
                                    HeapEntry* parent_entry,
@@ -990,13 +997,15 @@
  public:
   V8HeapExplorer(HeapSnapshot* snapshot,
                  SnapshottingProgressReportingInterface* progress);
-  ~V8HeapExplorer();
+  virtual ~V8HeapExplorer();
   virtual HeapEntry* AllocateEntry(
       HeapThing ptr, int children_count, int retainers_count);
   void AddRootEntries(SnapshotFillerInterface* filler);
   int EstimateObjectsCount();
   bool IterateAndExtractReferences(SnapshotFillerInterface* filler);

+  static HeapObject* const kInternalRootObject;
+
  private:
   HeapEntry* AddEntry(
       HeapObject* object, int children_count, int retainers_count);
@@ -1052,7 +1061,6 @@
   HeapObjectsSet known_references_;
   SnapshotFillerInterface* filler_;

-  static HeapObject* const kInternalRootObject;
   static HeapObject* const kGcRootsObject;

   friend class IndexedReferencesExtractor;
@@ -1062,6 +1070,54 @@
 };


+// An implementation of retained native objects extractor.
+class NativeObjectsExplorer : public HeapEntriesAllocator {
+ public:
+  NativeObjectsExplorer(HeapSnapshot* snapshot,
+                      SnapshottingProgressReportingInterface* progress);
+  virtual ~NativeObjectsExplorer();
+  virtual HeapEntry* AllocateEntry(
+      HeapThing ptr, int children_count, int retainers_count);
+  void AddRootEntries(SnapshotFillerInterface* filler);
+  int EstimateObjectsCount();
+  bool IterateAndExtractReferences(SnapshotFillerInterface* filler);
+
+ private:
+  void FillRetainedObjects();
+  List<HeapObject*>* GetListMaybeDisposeInfo(v8::RetainedObjectInfo* info);
+  void SetNativeRootReference(v8::RetainedObjectInfo* info);
+  void SetRootNativesRootReference();
+  void SetWrapperNativeReferences(HeapObject* wrapper,
+                                      v8::RetainedObjectInfo* info);
+  void VisitSubtreeWrapper(Object** p, uint16_t class_id);
+
+  static uint32_t InfoHash(v8::RetainedObjectInfo* info) {
+    return ComputeIntegerHash(static_cast<uint32_t>(info->GetHash()));
+  }
+  static bool RetainedInfosMatch(void* key1, void* key2) {
+    return key1 == key2 ||
+        (reinterpret_cast<v8::RetainedObjectInfo*>(key1))->IsEquivalent(
+            reinterpret_cast<v8::RetainedObjectInfo*>(key2));
+  }
+
+  HeapSnapshot* snapshot_;
+  HeapSnapshotsCollection* collection_;
+  SnapshottingProgressReportingInterface* progress_;
+  bool embedder_queried_;
+  HeapObjectsSet in_groups_;
+  // RetainedObjectInfo* -> List<HeapObject*>*
+  HashMap objects_by_info_;
+  // Used during references extraction.
+  SnapshotFillerInterface* filler_;
+
+  static HeapThing const kNativesRootObject;
+
+  friend class GlobalHandlesExtractor;
+
+  DISALLOW_COPY_AND_ASSIGN(NativeObjectsExplorer);
+};
+
+
class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface {
  public:
   HeapSnapshotGenerator(HeapSnapshot* snapshot,
@@ -1083,6 +1139,7 @@
   HeapSnapshot* snapshot_;
   v8::ActivityControl* control_;
   V8HeapExplorer v8_heap_explorer_;
+  NativeObjectsExplorer dom_explorer_;
   // Mapping from HeapThing pointers to HeapEntry* pointers.
   HeapEntriesMap entries_;
   // Used during snapshot generation.
=======================================
--- /branches/bleeding_edge/test/cctest/test-heap-profiler.cc Mon Dec 13 02:42:06 2010 +++ /branches/bleeding_edge/test/cctest/test-heap-profiler.cc Thu Mar 10 04:05:31 2011
@@ -1257,5 +1257,142 @@
   CHECK_EQ(control.total(), control.done());
   CHECK_GT(control.total(), 0);
 }
+
+
+namespace {
+
+class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
+ public:
+  TestRetainedObjectInfo(int hash,
+                         const char* label,
+                         intptr_t element_count = -1,
+                         intptr_t size = -1)
+      : disposed_(false),
+        hash_(hash),
+        label_(label),
+        element_count_(element_count),
+        size_(size) {
+    instances.Add(this);
+  }
+  virtual ~TestRetainedObjectInfo() {}
+  virtual void Dispose() {
+    CHECK(!disposed_);
+    disposed_ = true;
+  }
+  virtual bool IsEquivalent(RetainedObjectInfo* other) {
+    return GetHash() == other->GetHash();
+  }
+  virtual intptr_t GetHash() { return hash_; }
+  virtual const char* GetLabel() { return label_; }
+  virtual intptr_t GetElementCount() { return element_count_; }
+  virtual intptr_t GetSizeInBytes() { return size_; }
+  bool disposed() { return disposed_; }
+
+  static v8::RetainedObjectInfo* WrapperInfoCallback(
+      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)
+          return new TestRetainedObjectInfo(1, "aaa", 100);
+        else if (strcmp(*ascii, "BBB") == 0)
+          return new TestRetainedObjectInfo(1, "aaa", 100);
+      }
+    } else if (class_id == 2) {
+      if (wrapper->IsString()) {
+        v8::String::AsciiValue ascii(wrapper);
+        if (strcmp(*ascii, "CCC") == 0)
+          return new TestRetainedObjectInfo(2, "ccc");
+      }
+    }
+    CHECK(false);
+    return NULL;
+  }
+
+  static i::List<TestRetainedObjectInfo*> instances;
+
+ private:
+  bool disposed_;
+  int category_;
+  int hash_;
+  const char* label_;
+  intptr_t element_count_;
+  intptr_t size_;
+};
+
+
+i::List<TestRetainedObjectInfo*> TestRetainedObjectInfo::instances;
+}
+
+
+static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent,
+                                        v8::HeapGraphNode::Type type,
+                                        const char* name) {
+  for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) {
+    const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode();
+    if (node->GetType() == type && strcmp(name,
+               const_cast<i::HeapEntry*>(
+ reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) {
+      return node;
+    }
+  }
+  return NULL;
+}
+
+
+TEST(HeapSnapshotRetainedObjectInfo) {
+  v8::HandleScope scope;
+  LocalContext env;
+
+  v8::HeapProfiler::DefineWrapperClass(
+      1, TestRetainedObjectInfo::WrapperInfoCallback);
+  v8::HeapProfiler::DefineWrapperClass(
+      2, TestRetainedObjectInfo::WrapperInfoCallback);
+  v8::Persistent<v8::String> p_AAA =
+      v8::Persistent<v8::String>::New(v8_str("AAA"));
+  p_AAA.SetWrapperClassId(1);
+  v8::Persistent<v8::String> p_BBB =
+      v8::Persistent<v8::String>::New(v8_str("BBB"));
+  p_BBB.SetWrapperClassId(1);
+  v8::Persistent<v8::String> p_CCC =
+      v8::Persistent<v8::String>::New(v8_str("CCC"));
+  p_CCC.SetWrapperClassId(2);
+  CHECK_EQ(0, TestRetainedObjectInfo::instances.length());
+  const v8::HeapSnapshot* snapshot =
+      v8::HeapProfiler::TakeSnapshot(v8::String::New("retained"));
+
+  CHECK_EQ(3, TestRetainedObjectInfo::instances.length());
+  for (int i = 0; i < TestRetainedObjectInfo::instances.length(); ++i) {
+    CHECK(TestRetainedObjectInfo::instances[i]->disposed());
+    delete TestRetainedObjectInfo::instances[i];
+  }
+
+  const v8::HeapGraphNode* natives = GetNode(
+      snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(Native objects)");
+  CHECK_NE(NULL, natives);
+  CHECK_EQ(2, natives->GetChildrenCount());
+  const v8::HeapGraphNode* aaa = GetNode(
+      natives, v8::HeapGraphNode::kNative, "aaa / 100 entries");
+  CHECK_NE(NULL, aaa);
+  const v8::HeapGraphNode* ccc = GetNode(
+      natives, v8::HeapGraphNode::kNative, "ccc");
+  CHECK_NE(NULL, ccc);
+
+  CHECK_EQ(2, aaa->GetChildrenCount());
+  const v8::HeapGraphNode* n_AAA = GetNode(
+      aaa, v8::HeapGraphNode::kString, "AAA");
+  CHECK_NE(NULL, n_AAA);
+  const v8::HeapGraphNode* n_BBB = GetNode(
+      aaa, v8::HeapGraphNode::kString, "BBB");
+  CHECK_NE(NULL, n_BBB);
+  CHECK_EQ(1, ccc->GetChildrenCount());
+  const v8::HeapGraphNode* n_CCC = GetNode(
+      ccc, v8::HeapGraphNode::kString, "CCC");
+  CHECK_NE(NULL, n_CCC);
+
+ CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "Native")); + CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "Native")); + CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "Native"));
+}

 #endif  // ENABLE_LOGGING_AND_PROFILING
=======================================
--- /branches/bleeding_edge/test/cctest/test-mark-compact.cc Tue Mar 1 20:53:43 2011 +++ /branches/bleeding_edge/test/cctest/test-mark-compact.cc Thu Mar 10 04:05:31 2011
@@ -339,8 +339,8 @@
   {
     Object** g1_objects[] = { g1s1.location(), g1s2.location() };
     Object** g2_objects[] = { g2s1.location(), g2s2.location() };
-    GlobalHandles::AddGroup(g1_objects, 2);
-    GlobalHandles::AddGroup(g2_objects, 2);
+    GlobalHandles::AddGroup(g1_objects, 2, NULL);
+    GlobalHandles::AddGroup(g2_objects, 2, NULL);
   }
   // Do a full GC
   Heap::CollectGarbage(OLD_POINTER_SPACE);
@@ -357,8 +357,8 @@
   {
     Object** g1_objects[] = { g1s1.location(), g1s2.location() };
     Object** g2_objects[] = { g2s1.location(), g2s2.location() };
-    GlobalHandles::AddGroup(g1_objects, 2);
-    GlobalHandles::AddGroup(g2_objects, 2);
+    GlobalHandles::AddGroup(g1_objects, 2, NULL);
+    GlobalHandles::AddGroup(g2_objects, 2, NULL);
   }

   Heap::CollectGarbage(OLD_POINTER_SPACE);

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to