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(¤t->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