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 {
class V8EXPORT HeapGraphNode {
public:
enum Type {
kHidden = 0, // Hidden node, may be filtered when shown to user.
kArray = 1, // An array of elements.
kString = 2, // A string.
kObject = 3, // A JS object (except for arrays and strings).
kCode = 4, // Compiled code.
kClosure = 5, // Function closure.
kRegExp = 6, // RegExp.
kHeapNumber = 7 // Number stored in the heap.
kHidden = 0, // Hidden node, may be filtered when shown to user.
kArray = 1, // An array of elements.
kString = 2, // A string.
kObject = 3, // A JS object (except for arrays and strings).
kCode = 4, // Compiled code.
kClosure = 5, // Function closure.
kRegExp = 6, // RegExp.
kHeapNumber = 7, // Number stored in the heap.
kNative = 8 // Native object (not from V8 heap).
};
/** Returns node type (see HeapGraphNode::Type). */
......@@ -392,11 +393,22 @@ class V8EXPORT HeapSnapshot {
};
class RetainedObjectInfo;
/**
* Interface for controlling heap profiling.
*/
class V8EXPORT HeapProfiler {
public:
/**
* Callback function invoked for obtaining RetainedObjectInfo for
* the given JavaScript wrapper object. It is prohibited to enter V8
* while the callback is running: only getters on the handle and
* GetPointerFromInternalField on the objects are allowed.
*/
typedef RetainedObjectInfo* (*WrapperInfoCallback)
(uint16_t class_id, Handle<Value> wrapper);
/** Returns the number of snapshots taken. */
static int GetSnapshotsCount();
......@@ -414,6 +426,81 @@ class V8EXPORT HeapProfiler {
Handle<String> title,
HeapSnapshot::Type type = HeapSnapshot::kFull,
ActivityControl* control = NULL);
/** Binds a callback to embedder's class ID. */
static void DefineWrapperClass(
uint16_t class_id,
WrapperInfoCallback callback);
/**
* Default value of persistent handle class ID. Must not be used to
* define a class. Can be used to reset a class of a persistent
* handle.
*/
static const uint16_t kPersistentHandleNoClassId = 0;
};
/**
* Interface for providing information about embedder's objects
* held by global handles. This information is reported in two ways:
*
* 1. When calling AddObjectGroup, an embedder may pass
* RetainedObjectInfo instance describing the group. To collect
* this information while taking a heap snapshot, V8 calls GC
* prologue and epilogue callbacks.
*
* 2. When a heap snapshot is collected, V8 additionally
* requests RetainedObjectInfos for persistent handles that
* were not previously reported via AddObjectGroup.
*
* Thus, if an embedder wants to provide information about native
* objects for heap snapshots, he can do it in a GC prologue
* handler, and / or by assigning wrapper class ids in the following way:
*
* 1. Bind a callback to class id by calling DefineWrapperClass.
* 2. Call SetWrapperClassId on certain persistent handles.
*
* V8 takes ownership of RetainedObjectInfo instances passed to it and
* keeps them alive only during snapshot collection. Afterwards, they
* are freed by calling the Dispose class function.
*/
class V8EXPORT RetainedObjectInfo { // NOLINT
public:
/** Called by V8 when it no longer needs an instance. */
virtual void Dispose() = 0;
/** Returns whether two instances are equivalent. */
virtual bool IsEquivalent(RetainedObjectInfo* other) = 0;
/**
* Returns hash value for the instance. Equivalent instances
* must have the same hash value.
*/
virtual intptr_t GetHash() = 0;
/**
* Returns human-readable label. It must be a NUL-terminated UTF-8
* encoded string. V8 copies its contents during a call to GetLabel.
*/
virtual const char* GetLabel() = 0;
/**
* Returns element count in case if a global handle retains
* a subgraph by holding one of its nodes.
*/
virtual intptr_t GetElementCount() { return -1; }
/** Returns embedder's object size in bytes. */
virtual intptr_t GetSizeInBytes() { return -1; }
protected:
RetainedObjectInfo() {}
virtual ~RetainedObjectInfo() {}
private:
RetainedObjectInfo(const RetainedObjectInfo&);
RetainedObjectInfo& operator=(const RetainedObjectInfo&);
};
......
......@@ -396,6 +396,12 @@ template <class T> class Persistent : public Handle<T> {
*/
inline bool IsWeak() const;
/**
* Assigns a wrapper class ID to the handle. See RetainedObjectInfo
* interface description in v8-profiler.h for details.
*/
inline void SetWrapperClassId(uint16_t class_id);
private:
friend class ImplementationUtilities;
friend class ObjectTemplate;
......@@ -2534,6 +2540,8 @@ class V8EXPORT HeapStatistics {
};
class RetainedObjectInfo;
/**
* Container class for static utility functions.
*/
......@@ -2703,8 +2711,11 @@ class V8EXPORT V8 {
* intended to be used in the before-garbage-collection callback
* function, for instance to simulate DOM tree connections among JS
* wrapper objects.
* See v8-profiler.h for RetainedObjectInfo interface description.
*/
static void AddObjectGroup(Persistent<Value>* objects, size_t length);
static void AddObjectGroup(Persistent<Value>* objects,
size_t length,
RetainedObjectInfo* info = NULL);
/**
* Initializes from snapshot if possible. Otherwise, attempts to
......@@ -2913,6 +2924,8 @@ class V8EXPORT V8 {
static void ClearWeak(internal::Object** global_handle);
static bool IsGlobalNearDeath(internal::Object** global_handle);
static bool IsGlobalWeak(internal::Object** global_handle);
static void SetWrapperClassId(internal::Object** global_handle,
uint16_t class_id);
template <class T> friend class Handle;
template <class T> friend class Local;
......@@ -3561,6 +3574,10 @@ void Persistent<T>::ClearWeak() {
V8::ClearWeak(reinterpret_cast<internal::Object**>(**this));
}
template <class T>
void Persistent<T>::SetWrapperClassId(uint16_t class_id) {
V8::SetWrapperClassId(reinterpret_cast<internal::Object**>(**this), class_id);
}
Arguments::Arguments(internal::Object** implicit_args,
internal::Object** values, int length,
......
......@@ -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() {
ON_BAILOUT("v8::ObjectTemplate::NewInstance()", return Local<v8::Object>());
LOG_API("ObjectTemplate::NewInstance");
......@@ -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;
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,
*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
......
......@@ -41,6 +41,7 @@ class GlobalHandles::Node : public Malloced {
void Initialize(Object* object) {
// Set the initial value of the handle.
object_ = object;
class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
state_ = NORMAL;
parameter_or_next_free_.parameter = NULL;
callback_ = NULL;
......@@ -137,6 +138,14 @@ class GlobalHandles::Node : public Malloced {
return state_ == WEAK;
}
bool CanBeRetainer() {
return state_ != DESTROYED && state_ != NEAR_DEATH;
}
void SetWrapperClassId(uint16_t class_id) {
class_id_ = class_id;
}
// Returns the id for this weak handle.
void set_parameter(void* parameter) {
ASSERT(state_ != DESTROYED);
......@@ -190,6 +199,8 @@ class GlobalHandles::Node : public Malloced {
// Place the handle address first to avoid offset computation.
Object* object_; // Storage for object pointer.
uint16_t class_id_;
// Transition diagram:
// NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED }
enum State {
......@@ -199,7 +210,7 @@ class GlobalHandles::Node : public Malloced {
NEAR_DEATH, // Callback has informed the handle is near death.
DESTROYED
};
State state_;
State state_ : 3;
private:
// Handle specific callback.
......@@ -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) {
// Traversal of GC roots in the global handle list that are marked as
// WEAK or PENDING.
......@@ -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() {
// Reset all the lists.
set_head(NULL);
......@@ -515,8 +541,10 @@ List<ObjectGroup*>* GlobalHandles::ObjectGroups() {
return &groups;
}
void GlobalHandles::AddGroup(Object*** handles, size_t length) {
ObjectGroup* new_entry = new ObjectGroup(length);
void GlobalHandles::AddGroup(Object*** handles,
size_t length,
v8::RetainedObjectInfo* info) {
ObjectGroup* new_entry = new ObjectGroup(length, info);
for (size_t i = 0; i < length; ++i)
new_entry->objects_.Add(handles[i]);
ObjectGroups()->Add(new_entry);
......
......@@ -48,10 +48,16 @@ namespace internal {
class ObjectGroup : public Malloced {
public:
ObjectGroup() : objects_(4) {}
explicit ObjectGroup(size_t capacity)
: objects_(static_cast<int>(capacity)) { }
ObjectGroup(size_t capacity, v8::RetainedObjectInfo* info)
: objects_(static_cast<int>(capacity)),
info_(info) { }
~ObjectGroup() { if (info_ != NULL) info_->Dispose(); }
List<Object**> objects_;
v8::RetainedObjectInfo* info_;
private:
DISALLOW_COPY_AND_ASSIGN(ObjectGroup);
};
......@@ -75,6 +81,8 @@ class GlobalHandles : public AllStatic {
void* parameter,
WeakReferenceCallback callback);
static void SetWrapperClassId(Object** location, uint16_t class_id);
// Returns the current number of weak handles.
static int NumberOfWeakHandles() { return number_of_weak_handles_; }
......@@ -105,6 +113,9 @@ class GlobalHandles : public AllStatic {
// Iterates over all handles.
static void IterateAllRoots(ObjectVisitor* v);
// Iterates over all handles that have embedder-assigned class ID.
static void IterateAllRootsWithClassIds(ObjectVisitor* v);
// Iterates over all weak roots in heap.
static void IterateWeakRoots(ObjectVisitor* v);
......@@ -119,7 +130,9 @@ class GlobalHandles : public AllStatic {
// Add an object group.
// Should only used in GC callback function before a collection.
// All groups are destroyed after a mark-compact collection.
static void AddGroup(Object*** handles, size_t length);
static void AddGroup(Object*** handles,
size_t length,
v8::RetainedObjectInfo* info);
// Returns the object groups.
static List<ObjectGroup*>* ObjectGroups();
......
......@@ -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,
int type,
v8::ActivityControl* control) {
......@@ -401,7 +421,7 @@ HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name,
HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name,
int type,
v8::ActivityControl* control) {
return TakeSnapshotImpl(snapshots_->GetName(name), type, control);
return TakeSnapshotImpl(snapshots_->names()->GetName(name), type, control);
}
......@@ -872,7 +892,8 @@ class AllocatingConstructorHeapProfileIterator {
const NumberAndSizeInfo& number_and_size) {
const char* name = cluster.GetSpecialCaseName();
if (name == NULL) {
name = snapshot_->collection()->GetFunctionName(cluster.constructor());
name = snapshot_->collection()->names()->GetFunctionName(
cluster.constructor());
}
AddEntryFromAggregatedSnapshot(snapshot_,
root_child_index_,
......@@ -1013,7 +1034,8 @@ class AggregatedRetainerTreeAllocator : public HeapEntriesAllocator {
JSObjectsCluster cluster = HeapObjectAsCluster(obj);
const char* name = cluster.GetSpecialCaseName();
if (name == NULL) {
name = snapshot_->collection()->GetFunctionName(cluster.constructor());
name = snapshot_->collection()->names()->GetFunctionName(
cluster.constructor());
}
return AddEntryFromAggregatedSnapshot(
snapshot_, root_child_index_, HeapEntry::kObject, name,
......
......@@ -68,6 +68,11 @@ class HeapProfiler {
static void ObjectMoveEvent(Address from, Address to);
static void DefineWrapperClass(
uint16_t class_id, v8::HeapProfiler::WrapperInfoCallback callback);
static v8::RetainedObjectInfo* ExecuteWrapperClassCallback(uint16_t class_id,
Object** wrapper);
static INLINE(bool is_profiling()) {
return singleton_ != NULL && singleton_->snapshots_->is_tracking_objects();
}
......@@ -88,6 +93,7 @@ class HeapProfiler {
HeapSnapshotsCollection* snapshots_;
unsigned next_snapshot_uid_;
List<v8::HeapProfiler::WrapperInfoCallback> wrapper_callbacks_;
static HeapProfiler* singleton_;
#endif // ENABLE_LOGGING_AND_PROFILING
......
......@@ -1138,6 +1138,14 @@ class Heap : public AllStatic {
static GCTracer* tracer() { return tracer_; }
static void CallGlobalGCPrologueCallback() {
if (global_gc_prologue_callback_ != NULL) global_gc_prologue_callback_();
}
static void CallGlobalGCEpilogueCallback() {
if (global_gc_epilogue_callback_ != NULL) global_gc_epilogue_callback_();
}
private:
static int reserved_semispace_size_;
static int max_semispace_size_;
......
......@@ -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) {
uint32_t field = hash_field();
if (IsHashFieldComputed(field) && (field & kIsNotArrayIndexMask)) {
......
......@@ -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() {
// Should only be called if hash code has not yet been computed.
ASSERT(!HasHashCode());
......
......@@ -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
// few bits of information is moderately expensive, involving two memory
// loads where the second is dependent on the first. To improve efficiency
......@@ -6535,6 +6540,9 @@ class ObjectVisitor BASE_EMBEDDED {
VisitExternalReferences(p, p + 1);
}
// Visits a handle that has an embedder-assigned class ID.
virtual void VisitEmbedderReference(Object** p, uint16_t class_id) {}
#ifdef DEBUG
// Intended for serialization/deserialization checking: insert, or
// check for the presence of, a tag at this position in the stream.
......
This diff is collapsed.
......@@ -66,6 +66,9 @@ class 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(int index);
inline const char* GetFunctionName(String* name);
......@@ -76,11 +79,10 @@ class StringsStorage {
return strcmp(reinterpret_cast<char*>(key1),
reinterpret_cast<char*>(key2)) == 0;
}
const char* AddOrDisposeString(char* str, uint32_t hash);
// Mapping of strings by String::Hash to const char* strings.
HashMap names_;
// Mapping from ints to char* strings.
List<char*> index_names_;
DISALLOW_COPY_AND_ASSIGN(StringsStorage);
};
......@@ -517,7 +519,8 @@ class HeapEntry BASE_EMBEDDED {
kCode = v8::HeapGraphNode::kCode,
kClosure = v8::HeapGraphNode::kClosure,
kRegExp = v8::HeapGraphNode::kRegExp,
kHeapNumber = v8::HeapGraphNode::kHeapNumber
kHeapNumber = v8::HeapGraphNode::kHeapNumber,
kNative = v8::HeapGraphNode::kNative
};
HeapEntry() { }
......@@ -604,8 +607,8 @@ class HeapEntry BASE_EMBEDDED {
const char* TypeAsString();
unsigned painted_: 2;
unsigned type_: 3;
int children_count_: 27;
unsigned type_: 4;
int children_count_: 26;
int retainers_count_;
int self_size_;
union {
......@@ -677,6 +680,7 @@ class HeapSnapshot {
unsigned uid() { return uid_; }
HeapEntry* root() { return root_entry_; }
HeapEntry* gc_roots() { return gc_roots_entry_; }
HeapEntry* dom_subtrees_root() { return dom_subtrees_root_entry_; }
List<HeapEntry*>* entries() { return &entries_; }
void AllocateEntries(
......@@ -689,6 +693,7 @@ class HeapSnapshot {
int retainers_count);
HeapEntry* AddRootEntry(int children_count);
HeapEntry* AddGcRootsEntry(int children_count, int retainers_count);
HeapEntry* AddNativesRootEntry(int children_count, int retainers_count);
void ClearPaint();
HeapSnapshotsDiff* CompareWith(HeapSnapshot* snapshot);
HeapEntry* GetEntryById(uint64_t id);
......@@ -710,6 +715,7 @@ class HeapSnapshot {
unsigned uid_;
HeapEntry* root_entry_;
HeapEntry* gc_roots_entry_;
HeapEntry* dom_subtrees_root_entry_;
char* raw_entries_;
List<HeapEntry*> entries_;
bool entries_sorted_;
......@@ -733,8 +739,11 @@ class HeapObjectsMap {
uint64_t FindObject(Address addr);
void MoveObject(Address from, Address to);
static uint64_t GenerateId(v8::RetainedObjectInfo* info);
static const uint64_t kInternalRootObjectId;
static const uint64_t kGcRootsObjectId;
static const uint64_t kNativesRootObjectId;
static const uint64_t kFirstAvailableObjectId;
private:
......@@ -832,12 +841,7 @@ class HeapSnapshotsCollection {
List<HeapSnapshot*>* snapshots() { return &snapshots_; }
HeapSnapshot* GetSnapshot(unsigned uid);
const char* GetName(String* name) { return names_.GetName(name); }
const char* GetName(int index) { return names_.GetName(index); }
const char* GetFunctionName(String* name) {
return names_.GetFunctionName(name);
}
StringsStorage* names() { return &names_; }
TokenEnumerator* token_enumerator() { return token_enumerator_; }
uint64_t GetObjectId(Address addr) { return ids_.FindObject(addr); }
......@@ -950,8 +954,11 @@ class HeapObjectsSet {
class SnapshotFillerInterface {
public:
virtual ~SnapshotFillerInterface() { }
virtual HeapEntry* AddEntry(HeapThing ptr) = 0;
virtual HeapEntry* FindOrAddEntry(HeapThing ptr) = 0;
virtual HeapEntry* AddEntry(HeapThing ptr,
HeapEntriesAllocator* allocator) = 0;
virtual HeapEntry* FindEntry(HeapThing ptr) = 0;
virtual HeapEntry* FindOrAddEntry(HeapThing ptr,
HeapEntriesAllocator* allocator) = 0;
virtual void SetIndexedReference(HeapGraphEdge::Type type,
HeapThing parent_ptr,
HeapEntry* parent_entry,
......@@ -990,13 +997,15 @@ class V8HeapExplorer : public HeapEntriesAllocator {
public:
V8HeapExplorer(HeapSnapshot* snapshot,
SnapshottingProgressReportingInterface* progress);
~V8HeapExplorer();
virtual ~V8HeapExplorer();
virtual HeapEntry* AllocateEntry(
HeapThing ptr, int children_count, int retainers_count);
void AddRootEntries(SnapshotFillerInterface* filler);
int EstimateObjectsCount();
bool IterateAndExtractReferences(SnapshotFillerInterface* filler);
static HeapObject* const kInternalRootObject;
private:
HeapEntry* AddEntry(
HeapObject* object, int children_count, int retainers_count);
......@@ -1052,7 +1061,6 @@ class V8HeapExplorer : public HeapEntriesAllocator {
HeapObjectsSet known_references_;
SnapshotFillerInterface* filler_;
static HeapObject* const kInternalRootObject;
static HeapObject* const kGcRootsObject;
friend class IndexedReferencesExtractor;
......@@ -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 {
public:
HeapSnapshotGenerator(HeapSnapshot* snapshot,
......@@ -1083,6 +1139,7 @@ class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface {
HeapSnapshot* snapshot_;
v8::ActivityControl* control_;
V8HeapExplorer v8_heap_explorer_;
NativeObjectsExplorer dom_explorer_;
// Mapping from HeapThing pointers to HeapEntry* pointers.
HeapEntriesMap entries_;
// Used during snapshot generation.
......
......@@ -1258,4 +1258,141 @@ TEST(TakeHeapSnapshotAborting) {
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
......@@ -339,8 +339,8 @@ TEST(ObjectGroups) {
{
Object** g1_objects[] = { g1s1.location(), g1s2.location() };
Object** g2_objects[] = { g2s1.location(), g2s2.location() };
GlobalHandles::AddGroup(g1_objects, 2);
GlobalHandles::AddGroup(g2_objects, 2);
GlobalHandles::AddGroup(g1_objects, 2, NULL);
GlobalHandles::AddGroup(g2_objects, 2, NULL);
}
// Do a full GC
Heap::CollectGarbage(OLD_POINTER_SPACE);
......@@ -357,8 +357,8 @@ TEST(ObjectGroups) {
{
Object** g1_objects[] = { g1s1.location(), g1s2.location() };
Object** g2_objects[] = { g2s1.location(), g2s2.location() };
GlobalHandles::AddGroup(g1_objects, 2);
GlobalHandles::AddGroup(g2_objects, 2);
GlobalHandles::AddGroup(g1_objects, 2, NULL);
GlobalHandles::AddGroup(g2_objects, 2, NULL);
}
Heap::CollectGarbage(OLD_POINTER_SPACE);
......
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