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