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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7125 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 943e4f83
...@@ -245,14 +245,15 @@ class V8EXPORT HeapGraphPath { ...@@ -245,14 +245,15 @@ class V8EXPORT HeapGraphPath {
class V8EXPORT HeapGraphNode { class V8EXPORT HeapGraphNode {
public: public:
enum Type { enum Type {
kHidden = 0, // Hidden node, may be filtered when shown to user. kHidden = 0, // Hidden node, may be filtered when shown to user.
kArray = 1, // An array of elements. kArray = 1, // An array of elements.
kString = 2, // A string. kString = 2, // A string.
kObject = 3, // A JS object (except for arrays and strings). kObject = 3, // A JS object (except for arrays and strings).
kCode = 4, // Compiled code. kCode = 4, // Compiled code.
kClosure = 5, // Function closure. kClosure = 5, // Function closure.
kRegExp = 6, // RegExp. kRegExp = 6, // RegExp.
kHeapNumber = 7 // Number stored in the heap. kHeapNumber = 7, // Number stored in the heap.
kNative = 8 // Native object (not from V8 heap).
}; };
/** Returns node type (see HeapGraphNode::Type). */ /** Returns node type (see HeapGraphNode::Type). */
...@@ -392,11 +393,22 @@ class V8EXPORT HeapSnapshot { ...@@ -392,11 +393,22 @@ class V8EXPORT HeapSnapshot {
}; };
class RetainedObjectInfo;
/** /**
* Interface for controlling heap profiling. * Interface for controlling heap profiling.
*/ */
class V8EXPORT HeapProfiler { class V8EXPORT HeapProfiler {
public: 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. */ /** Returns the number of snapshots taken. */
static int GetSnapshotsCount(); static int GetSnapshotsCount();
...@@ -414,6 +426,81 @@ class V8EXPORT HeapProfiler { ...@@ -414,6 +426,81 @@ class V8EXPORT HeapProfiler {
Handle<String> title, Handle<String> title,
HeapSnapshot::Type type = HeapSnapshot::kFull, HeapSnapshot::Type type = HeapSnapshot::kFull,
ActivityControl* control = NULL); 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&);
}; };
......
...@@ -396,6 +396,12 @@ template <class T> class Persistent : public Handle<T> { ...@@ -396,6 +396,12 @@ template <class T> class Persistent : public Handle<T> {
*/ */
inline bool IsWeak() const; 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: private:
friend class ImplementationUtilities; friend class ImplementationUtilities;
friend class ObjectTemplate; friend class ObjectTemplate;
...@@ -2534,6 +2540,8 @@ class V8EXPORT HeapStatistics { ...@@ -2534,6 +2540,8 @@ class V8EXPORT HeapStatistics {
}; };
class RetainedObjectInfo;
/** /**
* Container class for static utility functions. * Container class for static utility functions.
*/ */
...@@ -2703,8 +2711,11 @@ class V8EXPORT V8 { ...@@ -2703,8 +2711,11 @@ class V8EXPORT V8 {
* intended to be used in the before-garbage-collection callback * intended to be used in the before-garbage-collection callback
* function, for instance to simulate DOM tree connections among JS * function, for instance to simulate DOM tree connections among JS
* wrapper objects. * 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 * Initializes from snapshot if possible. Otherwise, attempts to
...@@ -2913,6 +2924,8 @@ class V8EXPORT V8 { ...@@ -2913,6 +2924,8 @@ class V8EXPORT V8 {
static void ClearWeak(internal::Object** global_handle); static void ClearWeak(internal::Object** global_handle);
static bool IsGlobalNearDeath(internal::Object** global_handle); static bool IsGlobalNearDeath(internal::Object** global_handle);
static bool IsGlobalWeak(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 Handle;
template <class T> friend class Local; template <class T> friend class Local;
...@@ -3561,6 +3574,10 @@ void Persistent<T>::ClearWeak() { ...@@ -3561,6 +3574,10 @@ void Persistent<T>::ClearWeak() {
V8::ClearWeak(reinterpret_cast<internal::Object**>(**this)); 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, Arguments::Arguments(internal::Object** implicit_args,
internal::Object** values, int length, internal::Object** values, int length,
......
...@@ -3580,6 +3580,11 @@ void Context::ReattachGlobal(Handle<Object> global_object) { ...@@ -3580,6 +3580,11 @@ void Context::ReattachGlobal(Handle<Object> 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() { Local<v8::Object> ObjectTemplate::NewInstance() {
ON_BAILOUT("v8::ObjectTemplate::NewInstance()", return Local<v8::Object>()); ON_BAILOUT("v8::ObjectTemplate::NewInstance()", return Local<v8::Object>());
LOG_API("ObjectTemplate::NewInstance"); LOG_API("ObjectTemplate::NewInstance");
...@@ -4116,10 +4121,13 @@ void V8::SetFailedAccessCheckCallbackFunction( ...@@ -4116,10 +4121,13 @@ void V8::SetFailedAccessCheckCallbackFunction(
} }
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; if (IsDeadCheck("v8::V8::AddObjectGroup()")) return;
STATIC_ASSERT(sizeof(Persistent<Value>) == sizeof(i::Object**)); 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);
} }
...@@ -5065,6 +5073,12 @@ const HeapSnapshot* HeapProfiler::TakeSnapshot(Handle<String> title, ...@@ -5065,6 +5073,12 @@ const HeapSnapshot* HeapProfiler::TakeSnapshot(Handle<String> title,
*Utils::OpenHandle(*title), internal_type, control)); *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 #endif // ENABLE_LOGGING_AND_PROFILING
......
...@@ -41,6 +41,7 @@ class GlobalHandles::Node : public Malloced { ...@@ -41,6 +41,7 @@ class GlobalHandles::Node : public Malloced {
void Initialize(Object* object) { void Initialize(Object* object) {
// Set the initial value of the handle. // Set the initial value of the handle.
object_ = object; object_ = object;
class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
state_ = NORMAL; state_ = NORMAL;
parameter_or_next_free_.parameter = NULL; parameter_or_next_free_.parameter = NULL;
callback_ = NULL; callback_ = NULL;
...@@ -137,6 +138,14 @@ class GlobalHandles::Node : public Malloced { ...@@ -137,6 +138,14 @@ class GlobalHandles::Node : public Malloced {
return state_ == WEAK; 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. // Returns the id for this weak handle.
void set_parameter(void* parameter) { void set_parameter(void* parameter) {
ASSERT(state_ != DESTROYED); ASSERT(state_ != DESTROYED);
...@@ -190,6 +199,8 @@ class GlobalHandles::Node : public Malloced { ...@@ -190,6 +199,8 @@ class GlobalHandles::Node : public Malloced {
// Place the handle address first to avoid offset computation. // Place the handle address first to avoid offset computation.
Object* object_; // Storage for object pointer. Object* object_; // Storage for object pointer.
uint16_t class_id_;
// Transition diagram: // Transition diagram:
// NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED } // NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED }
enum State { enum State {
...@@ -199,7 +210,7 @@ class GlobalHandles::Node : public Malloced { ...@@ -199,7 +210,7 @@ class GlobalHandles::Node : public Malloced {
NEAR_DEATH, // Callback has informed the handle is near death. NEAR_DEATH, // Callback has informed the handle is near death.
DESTROYED DESTROYED
}; };
State state_; State state_ : 3;
private: private:
// Handle specific callback. // Handle specific callback.
...@@ -337,6 +348,11 @@ bool GlobalHandles::IsWeak(Object** location) { ...@@ -337,6 +348,11 @@ bool GlobalHandles::IsWeak(Object** location) {
} }
void GlobalHandles::SetWrapperClassId(Object** location, uint16_t class_id) {
Node::FromLocation(location)->SetWrapperClassId(class_id);
}
void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) { void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
// Traversal of GC roots in the global handle list that are marked as // Traversal of GC roots in the global handle list that are marked as
// WEAK or PENDING. // WEAK or PENDING.
...@@ -435,6 +451,16 @@ void GlobalHandles::IterateAllRoots(ObjectVisitor* v) { ...@@ -435,6 +451,16 @@ void GlobalHandles::IterateAllRoots(ObjectVisitor* v) {
} }
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() { void GlobalHandles::TearDown() {
// Reset all the lists. // Reset all the lists.
set_head(NULL); set_head(NULL);
...@@ -515,8 +541,10 @@ List<ObjectGroup*>* GlobalHandles::ObjectGroups() { ...@@ -515,8 +541,10 @@ List<ObjectGroup*>* GlobalHandles::ObjectGroups() {
return &groups; return &groups;
} }
void GlobalHandles::AddGroup(Object*** handles, size_t length) { void GlobalHandles::AddGroup(Object*** handles,
ObjectGroup* new_entry = new ObjectGroup(length); size_t length,
v8::RetainedObjectInfo* info) {
ObjectGroup* new_entry = new ObjectGroup(length, info);
for (size_t i = 0; i < length; ++i) for (size_t i = 0; i < length; ++i)
new_entry->objects_.Add(handles[i]); new_entry->objects_.Add(handles[i]);
ObjectGroups()->Add(new_entry); ObjectGroups()->Add(new_entry);
......
...@@ -48,10 +48,16 @@ namespace internal { ...@@ -48,10 +48,16 @@ namespace internal {
class ObjectGroup : public Malloced { class ObjectGroup : public Malloced {
public: public:
ObjectGroup() : objects_(4) {} ObjectGroup() : objects_(4) {}
explicit ObjectGroup(size_t capacity) ObjectGroup(size_t capacity, v8::RetainedObjectInfo* info)
: objects_(static_cast<int>(capacity)) { } : objects_(static_cast<int>(capacity)),
info_(info) { }
~ObjectGroup() { if (info_ != NULL) info_->Dispose(); }
List<Object**> objects_; List<Object**> objects_;
v8::RetainedObjectInfo* info_;
private:
DISALLOW_COPY_AND_ASSIGN(ObjectGroup);
}; };
...@@ -75,6 +81,8 @@ class GlobalHandles : public AllStatic { ...@@ -75,6 +81,8 @@ class GlobalHandles : public AllStatic {
void* parameter, void* parameter,
WeakReferenceCallback callback); WeakReferenceCallback callback);
static void SetWrapperClassId(Object** location, uint16_t class_id);
// Returns the current number of weak handles. // Returns the current number of weak handles.
static int NumberOfWeakHandles() { return number_of_weak_handles_; } static int NumberOfWeakHandles() { return number_of_weak_handles_; }
...@@ -105,6 +113,9 @@ class GlobalHandles : public AllStatic { ...@@ -105,6 +113,9 @@ class GlobalHandles : public AllStatic {
// Iterates over all handles. // Iterates over all handles.
static void IterateAllRoots(ObjectVisitor* v); 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. // Iterates over all weak roots in heap.
static void IterateWeakRoots(ObjectVisitor* v); static void IterateWeakRoots(ObjectVisitor* v);
...@@ -119,7 +130,9 @@ class GlobalHandles : public AllStatic { ...@@ -119,7 +130,9 @@ class GlobalHandles : public AllStatic {
// Add an object group. // Add an object group.
// Should only used in GC callback function before a collection. // Should only used in GC callback function before a collection.
// All groups are destroyed after a mark-compact 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. // Returns the object groups.
static List<ObjectGroup*>* ObjectGroups(); static List<ObjectGroup*>* ObjectGroups();
......
...@@ -364,6 +364,26 @@ HeapSnapshot* HeapProfiler::TakeSnapshot(String* name, ...@@ -364,6 +364,26 @@ HeapSnapshot* HeapProfiler::TakeSnapshot(String* name,
} }
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, HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name,
int type, int type,
v8::ActivityControl* control) { v8::ActivityControl* control) {
...@@ -401,7 +421,7 @@ HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name, ...@@ -401,7 +421,7 @@ HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name,
HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name, HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name,
int type, int type,
v8::ActivityControl* control) { v8::ActivityControl* control) {
return TakeSnapshotImpl(snapshots_->GetName(name), type, control); return TakeSnapshotImpl(snapshots_->names()->GetName(name), type, control);
} }
...@@ -872,7 +892,8 @@ class AllocatingConstructorHeapProfileIterator { ...@@ -872,7 +892,8 @@ class AllocatingConstructorHeapProfileIterator {
const NumberAndSizeInfo& number_and_size) { const NumberAndSizeInfo& number_and_size) {
const char* name = cluster.GetSpecialCaseName(); const char* name = cluster.GetSpecialCaseName();
if (name == NULL) { if (name == NULL) {
name = snapshot_->collection()->GetFunctionName(cluster.constructor()); name = snapshot_->collection()->names()->GetFunctionName(
cluster.constructor());
} }
AddEntryFromAggregatedSnapshot(snapshot_, AddEntryFromAggregatedSnapshot(snapshot_,
root_child_index_, root_child_index_,
...@@ -1013,7 +1034,8 @@ class AggregatedRetainerTreeAllocator : public HeapEntriesAllocator { ...@@ -1013,7 +1034,8 @@ class AggregatedRetainerTreeAllocator : public HeapEntriesAllocator {
JSObjectsCluster cluster = HeapObjectAsCluster(obj); JSObjectsCluster cluster = HeapObjectAsCluster(obj);
const char* name = cluster.GetSpecialCaseName(); const char* name = cluster.GetSpecialCaseName();
if (name == NULL) { if (name == NULL) {
name = snapshot_->collection()->GetFunctionName(cluster.constructor()); name = snapshot_->collection()->names()->GetFunctionName(
cluster.constructor());
} }
return AddEntryFromAggregatedSnapshot( return AddEntryFromAggregatedSnapshot(
snapshot_, root_child_index_, HeapEntry::kObject, name, snapshot_, root_child_index_, HeapEntry::kObject, name,
......
...@@ -68,6 +68,11 @@ class HeapProfiler { ...@@ -68,6 +68,11 @@ class HeapProfiler {
static void ObjectMoveEvent(Address from, Address to); 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()) { static INLINE(bool is_profiling()) {
return singleton_ != NULL && singleton_->snapshots_->is_tracking_objects(); return singleton_ != NULL && singleton_->snapshots_->is_tracking_objects();
} }
...@@ -88,6 +93,7 @@ class HeapProfiler { ...@@ -88,6 +93,7 @@ class HeapProfiler {
HeapSnapshotsCollection* snapshots_; HeapSnapshotsCollection* snapshots_;
unsigned next_snapshot_uid_; unsigned next_snapshot_uid_;
List<v8::HeapProfiler::WrapperInfoCallback> wrapper_callbacks_;
static HeapProfiler* singleton_; static HeapProfiler* singleton_;
#endif // ENABLE_LOGGING_AND_PROFILING #endif // ENABLE_LOGGING_AND_PROFILING
......
...@@ -1138,6 +1138,14 @@ class Heap : public AllStatic { ...@@ -1138,6 +1138,14 @@ class Heap : public AllStatic {
static GCTracer* tracer() { return tracer_; } 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: private:
static int reserved_semispace_size_; static int reserved_semispace_size_;
static int max_semispace_size_; static int max_semispace_size_;
......
...@@ -3668,6 +3668,22 @@ uint32_t StringHasher::GetHash() { ...@@ -3668,6 +3668,22 @@ uint32_t StringHasher::GetHash() {
} }
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) { bool String::AsArrayIndex(uint32_t* index) {
uint32_t field = hash_field(); uint32_t field = hash_field();
if (IsHashFieldComputed(field) && (field & kIsNotArrayIndexMask)) { if (IsHashFieldComputed(field) && (field & kIsNotArrayIndexMask)) {
......
...@@ -5238,22 +5238,6 @@ bool String::IsTwoByteEqualTo(Vector<const uc16> str) { ...@@ -5238,22 +5238,6 @@ bool String::IsTwoByteEqualTo(Vector<const uc16> str) {
} }
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() { uint32_t String::ComputeAndSetHash() {
// Should only be called if hash code has not yet been computed. // Should only be called if hash code has not yet been computed.
ASSERT(!HasHashCode()); ASSERT(!HasHashCode());
......
...@@ -5154,6 +5154,11 @@ class StringHasher { ...@@ -5154,6 +5154,11 @@ class StringHasher {
}; };
// 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 // The characteristics of a string are stored in its map. Retrieving these
// few bits of information is moderately expensive, involving two memory // few bits of information is moderately expensive, involving two memory
// loads where the second is dependent on the first. To improve efficiency // loads where the second is dependent on the first. To improve efficiency
...@@ -6535,6 +6540,9 @@ class ObjectVisitor BASE_EMBEDDED { ...@@ -6535,6 +6540,9 @@ class ObjectVisitor BASE_EMBEDDED {
VisitExternalReferences(p, p + 1); 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 #ifdef DEBUG
// Intended for serialization/deserialization checking: insert, or // Intended for serialization/deserialization checking: insert, or
// check for the presence of, a tag at this position in the stream. // check for the presence of, a tag at this position in the stream.
......
This diff is collapsed.
...@@ -66,6 +66,9 @@ class StringsStorage { ...@@ -66,6 +66,9 @@ class StringsStorage {
StringsStorage(); StringsStorage();
~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(String* name);
const char* GetName(int index); const char* GetName(int index);
inline const char* GetFunctionName(String* name); inline const char* GetFunctionName(String* name);
...@@ -76,11 +79,10 @@ class StringsStorage { ...@@ -76,11 +79,10 @@ class StringsStorage {
return strcmp(reinterpret_cast<char*>(key1), return strcmp(reinterpret_cast<char*>(key1),
reinterpret_cast<char*>(key2)) == 0; reinterpret_cast<char*>(key2)) == 0;
} }
const char* AddOrDisposeString(char* str, uint32_t hash);
// Mapping of strings by String::Hash to const char* strings. // Mapping of strings by String::Hash to const char* strings.
HashMap names_; HashMap names_;
// Mapping from ints to char* strings.
List<char*> index_names_;
DISALLOW_COPY_AND_ASSIGN(StringsStorage); DISALLOW_COPY_AND_ASSIGN(StringsStorage);
}; };
...@@ -517,7 +519,8 @@ class HeapEntry BASE_EMBEDDED { ...@@ -517,7 +519,8 @@ class HeapEntry BASE_EMBEDDED {
kCode = v8::HeapGraphNode::kCode, kCode = v8::HeapGraphNode::kCode,
kClosure = v8::HeapGraphNode::kClosure, kClosure = v8::HeapGraphNode::kClosure,
kRegExp = v8::HeapGraphNode::kRegExp, kRegExp = v8::HeapGraphNode::kRegExp,
kHeapNumber = v8::HeapGraphNode::kHeapNumber kHeapNumber = v8::HeapGraphNode::kHeapNumber,
kNative = v8::HeapGraphNode::kNative
}; };
HeapEntry() { } HeapEntry() { }
...@@ -604,8 +607,8 @@ class HeapEntry BASE_EMBEDDED { ...@@ -604,8 +607,8 @@ class HeapEntry BASE_EMBEDDED {
const char* TypeAsString(); const char* TypeAsString();
unsigned painted_: 2; unsigned painted_: 2;
unsigned type_: 3; unsigned type_: 4;
int children_count_: 27; int children_count_: 26;
int retainers_count_; int retainers_count_;
int self_size_; int self_size_;
union { union {
...@@ -677,6 +680,7 @@ class HeapSnapshot { ...@@ -677,6 +680,7 @@ class HeapSnapshot {
unsigned uid() { return uid_; } unsigned uid() { return uid_; }
HeapEntry* root() { return root_entry_; } HeapEntry* root() { return root_entry_; }
HeapEntry* gc_roots() { return gc_roots_entry_; } HeapEntry* gc_roots() { return gc_roots_entry_; }
HeapEntry* dom_subtrees_root() { return dom_subtrees_root_entry_; }
List<HeapEntry*>* entries() { return &entries_; } List<HeapEntry*>* entries() { return &entries_; }
void AllocateEntries( void AllocateEntries(
...@@ -689,6 +693,7 @@ class HeapSnapshot { ...@@ -689,6 +693,7 @@ class HeapSnapshot {
int retainers_count); int retainers_count);
HeapEntry* AddRootEntry(int children_count); HeapEntry* AddRootEntry(int children_count);
HeapEntry* AddGcRootsEntry(int children_count, int retainers_count); HeapEntry* AddGcRootsEntry(int children_count, int retainers_count);
HeapEntry* AddNativesRootEntry(int children_count, int retainers_count);
void ClearPaint(); void ClearPaint();
HeapSnapshotsDiff* CompareWith(HeapSnapshot* snapshot); HeapSnapshotsDiff* CompareWith(HeapSnapshot* snapshot);
HeapEntry* GetEntryById(uint64_t id); HeapEntry* GetEntryById(uint64_t id);
...@@ -710,6 +715,7 @@ class HeapSnapshot { ...@@ -710,6 +715,7 @@ class HeapSnapshot {
unsigned uid_; unsigned uid_;
HeapEntry* root_entry_; HeapEntry* root_entry_;
HeapEntry* gc_roots_entry_; HeapEntry* gc_roots_entry_;
HeapEntry* dom_subtrees_root_entry_;
char* raw_entries_; char* raw_entries_;
List<HeapEntry*> entries_; List<HeapEntry*> entries_;
bool entries_sorted_; bool entries_sorted_;
...@@ -733,8 +739,11 @@ class HeapObjectsMap { ...@@ -733,8 +739,11 @@ class HeapObjectsMap {
uint64_t FindObject(Address addr); uint64_t FindObject(Address addr);
void MoveObject(Address from, Address to); void MoveObject(Address from, Address to);
static uint64_t GenerateId(v8::RetainedObjectInfo* info);
static const uint64_t kInternalRootObjectId; static const uint64_t kInternalRootObjectId;
static const uint64_t kGcRootsObjectId; static const uint64_t kGcRootsObjectId;
static const uint64_t kNativesRootObjectId;
static const uint64_t kFirstAvailableObjectId; static const uint64_t kFirstAvailableObjectId;
private: private:
...@@ -832,12 +841,7 @@ class HeapSnapshotsCollection { ...@@ -832,12 +841,7 @@ class HeapSnapshotsCollection {
List<HeapSnapshot*>* snapshots() { return &snapshots_; } List<HeapSnapshot*>* snapshots() { return &snapshots_; }
HeapSnapshot* GetSnapshot(unsigned uid); HeapSnapshot* GetSnapshot(unsigned uid);
const char* GetName(String* name) { return names_.GetName(name); } StringsStorage* names() { return &names_; }
const char* GetName(int index) { return names_.GetName(index); }
const char* GetFunctionName(String* name) {
return names_.GetFunctionName(name);
}
TokenEnumerator* token_enumerator() { return token_enumerator_; } TokenEnumerator* token_enumerator() { return token_enumerator_; }
uint64_t GetObjectId(Address addr) { return ids_.FindObject(addr); } uint64_t GetObjectId(Address addr) { return ids_.FindObject(addr); }
...@@ -950,8 +954,11 @@ class HeapObjectsSet { ...@@ -950,8 +954,11 @@ class HeapObjectsSet {
class SnapshotFillerInterface { class SnapshotFillerInterface {
public: public:
virtual ~SnapshotFillerInterface() { } virtual ~SnapshotFillerInterface() { }
virtual HeapEntry* AddEntry(HeapThing ptr) = 0; virtual HeapEntry* AddEntry(HeapThing ptr,
virtual HeapEntry* FindOrAddEntry(HeapThing ptr) = 0; HeapEntriesAllocator* allocator) = 0;
virtual HeapEntry* FindEntry(HeapThing ptr) = 0;
virtual HeapEntry* FindOrAddEntry(HeapThing ptr,
HeapEntriesAllocator* allocator) = 0;
virtual void SetIndexedReference(HeapGraphEdge::Type type, virtual void SetIndexedReference(HeapGraphEdge::Type type,
HeapThing parent_ptr, HeapThing parent_ptr,
HeapEntry* parent_entry, HeapEntry* parent_entry,
...@@ -990,13 +997,15 @@ class V8HeapExplorer : public HeapEntriesAllocator { ...@@ -990,13 +997,15 @@ class V8HeapExplorer : public HeapEntriesAllocator {
public: public:
V8HeapExplorer(HeapSnapshot* snapshot, V8HeapExplorer(HeapSnapshot* snapshot,
SnapshottingProgressReportingInterface* progress); SnapshottingProgressReportingInterface* progress);
~V8HeapExplorer(); virtual ~V8HeapExplorer();
virtual HeapEntry* AllocateEntry( virtual HeapEntry* AllocateEntry(
HeapThing ptr, int children_count, int retainers_count); HeapThing ptr, int children_count, int retainers_count);
void AddRootEntries(SnapshotFillerInterface* filler); void AddRootEntries(SnapshotFillerInterface* filler);
int EstimateObjectsCount(); int EstimateObjectsCount();
bool IterateAndExtractReferences(SnapshotFillerInterface* filler); bool IterateAndExtractReferences(SnapshotFillerInterface* filler);
static HeapObject* const kInternalRootObject;
private: private:
HeapEntry* AddEntry( HeapEntry* AddEntry(
HeapObject* object, int children_count, int retainers_count); HeapObject* object, int children_count, int retainers_count);
...@@ -1052,7 +1061,6 @@ class V8HeapExplorer : public HeapEntriesAllocator { ...@@ -1052,7 +1061,6 @@ class V8HeapExplorer : public HeapEntriesAllocator {
HeapObjectsSet known_references_; HeapObjectsSet known_references_;
SnapshotFillerInterface* filler_; SnapshotFillerInterface* filler_;
static HeapObject* const kInternalRootObject;
static HeapObject* const kGcRootsObject; static HeapObject* const kGcRootsObject;
friend class IndexedReferencesExtractor; friend class IndexedReferencesExtractor;
...@@ -1062,6 +1070,54 @@ class V8HeapExplorer : public HeapEntriesAllocator { ...@@ -1062,6 +1070,54 @@ class V8HeapExplorer : public HeapEntriesAllocator {
}; };
// 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 { class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface {
public: public:
HeapSnapshotGenerator(HeapSnapshot* snapshot, HeapSnapshotGenerator(HeapSnapshot* snapshot,
...@@ -1083,6 +1139,7 @@ class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface { ...@@ -1083,6 +1139,7 @@ class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface {
HeapSnapshot* snapshot_; HeapSnapshot* snapshot_;
v8::ActivityControl* control_; v8::ActivityControl* control_;
V8HeapExplorer v8_heap_explorer_; V8HeapExplorer v8_heap_explorer_;
NativeObjectsExplorer dom_explorer_;
// Mapping from HeapThing pointers to HeapEntry* pointers. // Mapping from HeapThing pointers to HeapEntry* pointers.
HeapEntriesMap entries_; HeapEntriesMap entries_;
// Used during snapshot generation. // Used during snapshot generation.
......
...@@ -1258,4 +1258,141 @@ TEST(TakeHeapSnapshotAborting) { ...@@ -1258,4 +1258,141 @@ TEST(TakeHeapSnapshotAborting) {
CHECK_GT(control.total(), 0); 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 #endif // ENABLE_LOGGING_AND_PROFILING
...@@ -339,8 +339,8 @@ TEST(ObjectGroups) { ...@@ -339,8 +339,8 @@ TEST(ObjectGroups) {
{ {
Object** g1_objects[] = { g1s1.location(), g1s2.location() }; Object** g1_objects[] = { g1s1.location(), g1s2.location() };
Object** g2_objects[] = { g2s1.location(), g2s2.location() }; Object** g2_objects[] = { g2s1.location(), g2s2.location() };
GlobalHandles::AddGroup(g1_objects, 2); GlobalHandles::AddGroup(g1_objects, 2, NULL);
GlobalHandles::AddGroup(g2_objects, 2); GlobalHandles::AddGroup(g2_objects, 2, NULL);
} }
// Do a full GC // Do a full GC
Heap::CollectGarbage(OLD_POINTER_SPACE); Heap::CollectGarbage(OLD_POINTER_SPACE);
...@@ -357,8 +357,8 @@ TEST(ObjectGroups) { ...@@ -357,8 +357,8 @@ TEST(ObjectGroups) {
{ {
Object** g1_objects[] = { g1s1.location(), g1s2.location() }; Object** g1_objects[] = { g1s1.location(), g1s2.location() };
Object** g2_objects[] = { g2s1.location(), g2s2.location() }; Object** g2_objects[] = { g2s1.location(), g2s2.location() };
GlobalHandles::AddGroup(g1_objects, 2); GlobalHandles::AddGroup(g1_objects, 2, NULL);
GlobalHandles::AddGroup(g2_objects, 2); GlobalHandles::AddGroup(g2_objects, 2, NULL);
} }
Heap::CollectGarbage(OLD_POINTER_SPACE); Heap::CollectGarbage(OLD_POINTER_SPACE);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment