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.
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "v8.h" #include "v8.h"
#include "global-handles.h" #include "global-handles.h"
#include "heap-profiler.h"
#include "scopeinfo.h" #include "scopeinfo.h"
#include "top.h" #include "top.h"
#include "unicode.h" #include "unicode.h"
...@@ -94,51 +95,70 @@ StringsStorage::StringsStorage() ...@@ -94,51 +95,70 @@ StringsStorage::StringsStorage()
} }
static void DeleteIndexName(char** name_ptr) {
DeleteArray(*name_ptr);
}
StringsStorage::~StringsStorage() { StringsStorage::~StringsStorage() {
for (HashMap::Entry* p = names_.Start(); for (HashMap::Entry* p = names_.Start();
p != NULL; p != NULL;
p = names_.Next(p)) { p = names_.Next(p)) {
DeleteArray(reinterpret_cast<const char*>(p->value)); 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) { const char* StringsStorage::GetName(String* name) {
if (name->IsString()) { if (name->IsString()) {
char* c_name = return AddOrDisposeString(
name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).Detach(); name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).Detach(),
HashMap::Entry* cache_entry = names_.Lookup(c_name, name->Hash(), true); name->Hash());
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 ""; return "";
} }
const char* StringsStorage::GetName(int index) { const char* StringsStorage::GetName(int index) {
ASSERT(index >= 0); return GetFormatted("%d", index);
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];
} }
...@@ -1009,6 +1029,7 @@ const char* HeapEntry::TypeAsString() { ...@@ -1009,6 +1029,7 @@ const char* HeapEntry::TypeAsString() {
case kArray: return "/array/"; case kArray: return "/array/";
case kRegExp: return "/regexp/"; case kRegExp: return "/regexp/";
case kHeapNumber: return "/number/"; case kHeapNumber: return "/number/";
case kNative: return "/native/";
default: return "???"; default: return "???";
} }
} }
...@@ -1205,6 +1226,7 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, ...@@ -1205,6 +1226,7 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
uid_(uid), uid_(uid),
root_entry_(NULL), root_entry_(NULL),
gc_roots_entry_(NULL), gc_roots_entry_(NULL),
dom_subtrees_root_entry_(NULL),
raw_entries_(NULL), raw_entries_(NULL),
entries_sorted_(false), entries_sorted_(false),
retaining_paths_(HeapEntry::Match) { retaining_paths_(HeapEntry::Match) {
...@@ -1279,6 +1301,19 @@ HeapEntry* HeapSnapshot::AddGcRootsEntry(int children_count, ...@@ -1279,6 +1301,19 @@ HeapEntry* HeapSnapshot::AddGcRootsEntry(int children_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, HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
const char* name, const char* name,
uint64_t id, uint64_t id,
...@@ -1372,10 +1407,13 @@ void HeapSnapshot::Print(int max_depth) { ...@@ -1372,10 +1407,13 @@ void HeapSnapshot::Print(int max_depth) {
} }
const uint64_t HeapObjectsMap::kInternalRootObjectId = 0; // We split IDs on evens for embedder objects (see
const uint64_t HeapObjectsMap::kGcRootsObjectId = 1; // 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. // Increase kFirstAvailableObjectId if new 'special' objects appear.
const uint64_t HeapObjectsMap::kFirstAvailableObjectId = 2; const uint64_t HeapObjectsMap::kFirstAvailableObjectId = 7;
HeapObjectsMap::HeapObjectsMap() HeapObjectsMap::HeapObjectsMap()
: initial_fill_mode_(true), : initial_fill_mode_(true),
...@@ -1400,7 +1438,8 @@ uint64_t HeapObjectsMap::FindObject(Address addr) { ...@@ -1400,7 +1438,8 @@ uint64_t HeapObjectsMap::FindObject(Address addr) {
uint64_t existing = FindEntry(addr); uint64_t existing = FindEntry(addr);
if (existing != 0) return existing; if (existing != 0) return existing;
} }
uint64_t id = next_id_++; uint64_t id = next_id_;
next_id_ += 2;
AddEntry(addr, id); AddEntry(addr, id);
return id; return id;
} }
...@@ -1468,6 +1507,17 @@ void HeapObjectsMap::RemoveDeadEntries() { ...@@ -1468,6 +1507,17 @@ void HeapObjectsMap::RemoveDeadEntries() {
} }
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() HeapSnapshotsCollection::HeapSnapshotsCollection()
: is_tracking_objects_(false), : is_tracking_objects_(false),
snapshots_uids_(HeapSnapshotsMatch), snapshots_uids_(HeapSnapshotsMatch),
...@@ -1551,6 +1601,8 @@ void HeapEntriesMap::AllocateEntries() { ...@@ -1551,6 +1601,8 @@ void HeapEntriesMap::AllocateEntries() {
p->key, p->key,
entry_info->children_count, entry_info->children_count,
entry_info->retainers_count); entry_info->retainers_count);
ASSERT(entry_info->entry != NULL);
ASSERT(entry_info->entry != kHeapEntryPlaceholder);
entry_info->children_count = 0; entry_info->children_count = 0;
entry_info->retainers_count = 0; entry_info->retainers_count = 0;
} }
...@@ -1630,9 +1682,11 @@ void HeapObjectsSet::Insert(Object* obj) { ...@@ -1630,9 +1682,11 @@ void HeapObjectsSet::Insert(Object* obj) {
HeapObject *const V8HeapExplorer::kInternalRootObject = HeapObject *const V8HeapExplorer::kInternalRootObject =
reinterpret_cast<HeapObject*>(1); reinterpret_cast<HeapObject*>(
static_cast<intptr_t>(HeapObjectsMap::kInternalRootObjectId));
HeapObject *const V8HeapExplorer::kGcRootsObject = HeapObject *const V8HeapExplorer::kGcRootsObject =
reinterpret_cast<HeapObject*>(2); reinterpret_cast<HeapObject*>(
static_cast<intptr_t>(HeapObjectsMap::kGcRootsObjectId));
V8HeapExplorer::V8HeapExplorer( V8HeapExplorer::V8HeapExplorer(
...@@ -1669,27 +1723,28 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object, ...@@ -1669,27 +1723,28 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
SharedFunctionInfo* shared = func->shared(); SharedFunctionInfo* shared = func->shared();
return AddEntry(object, return AddEntry(object,
HeapEntry::kClosure, HeapEntry::kClosure,
collection_->GetName(String::cast(shared->name())), collection_->names()->GetName(String::cast(shared->name())),
children_count, children_count,
retainers_count); retainers_count);
} else if (object->IsJSRegExp()) { } else if (object->IsJSRegExp()) {
JSRegExp* re = JSRegExp::cast(object); JSRegExp* re = JSRegExp::cast(object);
return AddEntry(object, return AddEntry(object,
HeapEntry::kRegExp, HeapEntry::kRegExp,
collection_->GetName(re->Pattern()), collection_->names()->GetName(re->Pattern()),
children_count, children_count,
retainers_count); retainers_count);
} else if (object->IsJSObject()) { } else if (object->IsJSObject()) {
return AddEntry(object, return AddEntry(object,
HeapEntry::kObject, HeapEntry::kObject,
collection_->GetName(GetConstructorNameForHeapProfile( collection_->names()->GetName(
JSObject::cast(object))), GetConstructorNameForHeapProfile(
JSObject::cast(object))),
children_count, children_count,
retainers_count); retainers_count);
} else if (object->IsString()) { } else if (object->IsString()) {
return AddEntry(object, return AddEntry(object,
HeapEntry::kString, HeapEntry::kString,
collection_->GetName(String::cast(object)), collection_->names()->GetName(String::cast(object)),
children_count, children_count,
retainers_count); retainers_count);
} else if (object->IsCode()) { } else if (object->IsCode()) {
...@@ -1702,7 +1757,7 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object, ...@@ -1702,7 +1757,7 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
SharedFunctionInfo* shared = SharedFunctionInfo::cast(object); SharedFunctionInfo* shared = SharedFunctionInfo::cast(object);
return AddEntry(object, return AddEntry(object,
HeapEntry::kCode, HeapEntry::kCode,
collection_->GetName(String::cast(shared->name())), collection_->names()->GetName(String::cast(shared->name())),
children_count, children_count,
retainers_count); retainers_count);
} else if (object->IsScript()) { } else if (object->IsScript()) {
...@@ -1710,7 +1765,9 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object, ...@@ -1710,7 +1765,9 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
return AddEntry(object, return AddEntry(object,
HeapEntry::kCode, HeapEntry::kCode,
script->name()->IsString() ? script->name()->IsString() ?
collection_->GetName(String::cast(script->name())) : "", collection_->names()->GetName(
String::cast(script->name()))
: "",
children_count, children_count,
retainers_count); retainers_count);
} else if (object->IsFixedArray()) { } else if (object->IsFixedArray()) {
...@@ -1749,8 +1806,8 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object, ...@@ -1749,8 +1806,8 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) { void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) {
filler->AddEntry(kInternalRootObject); filler->AddEntry(kInternalRootObject, this);
filler->AddEntry(kGcRootsObject); filler->AddEntry(kGcRootsObject, this);
} }
...@@ -1940,7 +1997,7 @@ void V8HeapExplorer::ExtractInternalReferences(JSObject* js_obj, ...@@ -1940,7 +1997,7 @@ void V8HeapExplorer::ExtractInternalReferences(JSObject* js_obj,
HeapEntry* V8HeapExplorer::GetEntry(Object* obj) { HeapEntry* V8HeapExplorer::GetEntry(Object* obj) {
if (!obj->IsHeapObject()) return NULL; if (!obj->IsHeapObject()) return NULL;
return filler_->FindOrAddEntry(obj); return filler_->FindOrAddEntry(obj, this);
} }
...@@ -1992,7 +2049,7 @@ void V8HeapExplorer::SetClosureReference(HeapObject* parent_obj, ...@@ -1992,7 +2049,7 @@ void V8HeapExplorer::SetClosureReference(HeapObject* parent_obj,
filler_->SetNamedReference(HeapGraphEdge::kContextVariable, filler_->SetNamedReference(HeapGraphEdge::kContextVariable,
parent_obj, parent_obj,
parent_entry, parent_entry,
collection_->GetName(reference_name), collection_->names()->GetName(reference_name),
child_obj, child_obj,
child_entry); child_entry);
known_references_.Insert(child_obj); known_references_.Insert(child_obj);
...@@ -2043,7 +2100,7 @@ void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj, ...@@ -2043,7 +2100,7 @@ void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
filler_->SetNamedReference(HeapGraphEdge::kInternal, filler_->SetNamedReference(HeapGraphEdge::kInternal,
parent_obj, parent_obj,
parent_entry, parent_entry,
collection_->GetName(index), collection_->names()->GetName(index),
child_obj, child_obj,
child_entry); child_entry);
known_references_.Insert(child_obj); known_references_.Insert(child_obj);
...@@ -2078,7 +2135,7 @@ void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj, ...@@ -2078,7 +2135,7 @@ void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj,
filler_->SetNamedReference(type, filler_->SetNamedReference(type,
parent_obj, parent_obj,
parent_entry, parent_entry,
collection_->GetName(reference_name), collection_->names()->GetName(reference_name),
child_obj, child_obj,
child_entry); child_entry);
known_references_.Insert(child_obj); known_references_.Insert(child_obj);
...@@ -2096,7 +2153,7 @@ void V8HeapExplorer::SetPropertyShortcutReference( ...@@ -2096,7 +2153,7 @@ void V8HeapExplorer::SetPropertyShortcutReference(
filler_->SetNamedReference(HeapGraphEdge::kShortcut, filler_->SetNamedReference(HeapGraphEdge::kShortcut,
parent_obj, parent_obj,
parent_entry, parent_entry,
collection_->GetName(reference_name), collection_->names()->GetName(reference_name),
child_obj, child_obj,
child_entry); child_entry);
} }
...@@ -2132,25 +2189,215 @@ void V8HeapExplorer::SetGcRootsReference(Object* child_obj) { ...@@ -2132,25 +2189,215 @@ void V8HeapExplorer::SetGcRootsReference(Object* child_obj) {
} }
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, HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot,
v8::ActivityControl* control) v8::ActivityControl* control)
: snapshot_(snapshot), : snapshot_(snapshot),
control_(control), control_(control),
v8_heap_explorer_(snapshot_, this) { v8_heap_explorer_(snapshot_, this),
dom_explorer_(snapshot_, this) {
} }
class SnapshotCounter : public SnapshotFillerInterface { class SnapshotCounter : public SnapshotFillerInterface {
public: public:
SnapshotCounter(HeapEntriesAllocator* allocator, HeapEntriesMap* entries) explicit SnapshotCounter(HeapEntriesMap* entries) : entries_(entries) { }
: allocator_(allocator), entries_(entries) { } HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
HeapEntry* AddEntry(HeapThing ptr) { entries_->Pair(ptr, allocator, HeapEntriesMap::kHeapEntryPlaceholder);
entries_->Pair(ptr, allocator_, HeapEntriesMap::kHeapEntryPlaceholder);
return HeapEntriesMap::kHeapEntryPlaceholder; return HeapEntriesMap::kHeapEntryPlaceholder;
} }
HeapEntry* FindOrAddEntry(HeapThing ptr) { HeapEntry* FindEntry(HeapThing ptr) {
HeapEntry* entry = entries_->Map(ptr); return entries_->Map(ptr);
return entry != NULL ? entry : AddEntry(ptr); }
HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
HeapEntry* entry = FindEntry(ptr);
return entry != NULL ? entry : AddEntry(ptr, allocator);
} }
void SetIndexedReference(HeapGraphEdge::Type, void SetIndexedReference(HeapGraphEdge::Type,
HeapThing parent_ptr, HeapThing parent_ptr,
...@@ -2183,7 +2430,6 @@ class SnapshotCounter : public SnapshotFillerInterface { ...@@ -2183,7 +2430,6 @@ class SnapshotCounter : public SnapshotFillerInterface {
entries_->CountReference(parent_ptr, child_ptr); entries_->CountReference(parent_ptr, child_ptr);
} }
private: private:
HeapEntriesAllocator* allocator_;
HeapEntriesMap* entries_; HeapEntriesMap* entries_;
}; };
...@@ -2194,13 +2440,16 @@ class SnapshotFiller : public SnapshotFillerInterface { ...@@ -2194,13 +2440,16 @@ class SnapshotFiller : public SnapshotFillerInterface {
: snapshot_(snapshot), : snapshot_(snapshot),
collection_(snapshot->collection()), collection_(snapshot->collection()),
entries_(entries) { } entries_(entries) { }
HeapEntry* AddEntry(HeapThing ptr) { HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
UNREACHABLE(); UNREACHABLE();
return NULL; return NULL;
} }
HeapEntry* FindOrAddEntry(HeapThing ptr) { HeapEntry* FindEntry(HeapThing ptr) {
HeapEntry* entry = entries_->Map(ptr); return entries_->Map(ptr);
return entry != NULL ? entry : AddEntry(ptr); }
HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
HeapEntry* entry = FindEntry(ptr);
return entry != NULL ? entry : AddEntry(ptr, allocator);
} }
void SetIndexedReference(HeapGraphEdge::Type type, void SetIndexedReference(HeapGraphEdge::Type type,
HeapThing parent_ptr, HeapThing parent_ptr,
...@@ -2247,7 +2496,7 @@ class SnapshotFiller : public SnapshotFillerInterface { ...@@ -2247,7 +2496,7 @@ class SnapshotFiller : public SnapshotFillerInterface {
parent_ptr, child_ptr, &child_index, &retainer_index); parent_ptr, child_ptr, &child_index, &retainer_index);
parent_entry->SetNamedReference(type, parent_entry->SetNamedReference(type,
child_index, child_index,
collection_->GetName(child_index + 1), collection_->names()->GetName(child_index + 1),
child_entry, child_entry,
retainer_index); retainer_index);
} }
...@@ -2303,21 +2552,28 @@ bool HeapSnapshotGenerator::ProgressReport(bool force) { ...@@ -2303,21 +2552,28 @@ bool HeapSnapshotGenerator::ProgressReport(bool force) {
void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) { void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) {
if (control_ == NULL) return; 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; progress_counter_ = 0;
} }
bool HeapSnapshotGenerator::CountEntriesAndReferences() { bool HeapSnapshotGenerator::CountEntriesAndReferences() {
SnapshotCounter counter(&v8_heap_explorer_, &entries_); SnapshotCounter counter(&entries_);
v8_heap_explorer_.AddRootEntries(&counter); 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() { bool HeapSnapshotGenerator::FillReferences() {
SnapshotFiller filler(snapshot_, &entries_); SnapshotFiller filler(snapshot_, &entries_);
return v8_heap_explorer_.IterateAndExtractReferences(&filler); return
v8_heap_explorer_.IterateAndExtractReferences(&filler) &&
dom_explorer_.IterateAndExtractReferences(&filler);
} }
...@@ -2735,7 +2991,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() { ...@@ -2735,7 +2991,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() {
"," JSON_S("code") "," JSON_S("code")
"," JSON_S("closure") "," JSON_S("closure")
"," JSON_S("regexp") "," JSON_S("regexp")
"," JSON_S("number")) "," JSON_S("number")
"," JSON_S("native"))
"," JSON_S("string") "," JSON_S("string")
"," JSON_S("number") "," JSON_S("number")
"," JSON_S("number") "," JSON_S("number")
......
...@@ -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