Adding files for LiveObjectList implementation.
Patch by Mark Lam from Hewlett-Packard Development Company, LP Review URL: http://codereview.chromium.org/6357005 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7012 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
8481a75698
commit
9c161bcf21
@ -267,6 +267,12 @@ DEFINE_bool(use_idle_notification, true,
|
||||
// ic.cc
|
||||
DEFINE_bool(use_ic, true, "use inline caching")
|
||||
|
||||
#ifdef LIVE_OBJECT_LIST
|
||||
// liveobjectlist.cc
|
||||
DEFINE_string(lol_workdir, NULL, "path for lol temp files")
|
||||
DEFINE_bool(verify_lol, false, "perform debugging verification for lol")
|
||||
#endif
|
||||
|
||||
// macro-assembler-ia32.cc
|
||||
DEFINE_bool(native_code_counters, false,
|
||||
"generate extra code for manipulating stats counters")
|
||||
|
@ -32,5 +32,95 @@
|
||||
|
||||
#include "liveobjectlist.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
#ifdef LIVE_OBJECT_LIST
|
||||
|
||||
void LiveObjectList::GCEpilogue() {
|
||||
if (!NeedLOLProcessing()) return;
|
||||
GCEpiloguePrivate();
|
||||
}
|
||||
|
||||
|
||||
void LiveObjectList::GCPrologue() {
|
||||
if (!NeedLOLProcessing()) return;
|
||||
#ifdef VERIFY_LOL
|
||||
if (FLAG_verify_lol) {
|
||||
Verify();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void LiveObjectList::IterateElements(ObjectVisitor* v) {
|
||||
if (!NeedLOLProcessing()) return;
|
||||
IterateElementsPrivate(v);
|
||||
}
|
||||
|
||||
|
||||
void LiveObjectList::ProcessNonLive(HeapObject *obj) {
|
||||
// Only do work if we have at least one list to process.
|
||||
if (last()) DoProcessNonLive(obj);
|
||||
}
|
||||
|
||||
|
||||
void LiveObjectList::UpdateReferencesForScavengeGC() {
|
||||
if (LiveObjectList::NeedLOLProcessing()) {
|
||||
UpdateLiveObjectListVisitor update_visitor;
|
||||
LiveObjectList::IterateElements(&update_visitor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LiveObjectList* LiveObjectList::FindLolForId(int id,
|
||||
LiveObjectList* start_lol) {
|
||||
if (id != 0) {
|
||||
LiveObjectList* lol = start_lol;
|
||||
while (lol != NULL) {
|
||||
if (lol->id() == id) {
|
||||
return lol;
|
||||
}
|
||||
lol = lol->prev_;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// Iterates the elements in every lol and returns the one that matches the
|
||||
// specified key. If no matching element is found, then it returns NULL.
|
||||
template <typename T>
|
||||
inline LiveObjectList::Element*
|
||||
LiveObjectList::FindElementFor(T (*GetValue)(LiveObjectList::Element*), T key) {
|
||||
LiveObjectList *lol = last();
|
||||
while (lol != NULL) {
|
||||
Element* elements = lol->elements_;
|
||||
for (int i = 0; i < lol->obj_count_; i++) {
|
||||
Element* element = &elements[i];
|
||||
if (GetValue(element) == key) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
lol = lol->prev_;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
inline int LiveObjectList::GetElementId(LiveObjectList::Element* element) {
|
||||
return element->id_;
|
||||
}
|
||||
|
||||
|
||||
inline HeapObject*
|
||||
LiveObjectList::GetElementObj(LiveObjectList::Element* element) {
|
||||
return element->obj_;
|
||||
}
|
||||
|
||||
#endif // LIVE_OBJECT_LIST
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_LIVEOBJECTLIST_INL_H_
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -40,54 +40,225 @@ namespace internal {
|
||||
|
||||
#ifdef LIVE_OBJECT_LIST
|
||||
|
||||
#ifdef DEBUG
|
||||
// The following symbol when defined enables thorough verification of lol data.
|
||||
// FLAG_verify_lol will also need to set to true to enable the verification.
|
||||
#define VERIFY_LOL
|
||||
#endif
|
||||
|
||||
// Temporary stubbed out LiveObjectList implementation.
|
||||
|
||||
typedef int LiveObjectType;
|
||||
class LolFilter;
|
||||
class LiveObjectSummary;
|
||||
class DumpWriter;
|
||||
class SummaryWriter;
|
||||
|
||||
|
||||
// The LiveObjectList is both a mechanism for tracking a live capture of
|
||||
// objects in the JS heap, as well as is the data structure which represents
|
||||
// each of those captures. Unlike a snapshot, the lol is live. For example,
|
||||
// if an object in a captured lol dies and is collected by the GC, the lol
|
||||
// will reflect that the object is no longer available. The term
|
||||
// LiveObjectList (and lol) is used to describe both the mechanism and the
|
||||
// data structure depending on context of use.
|
||||
//
|
||||
// In captured lols, objects are tracked using their address and an object id.
|
||||
// The object id is unique. Once assigned to an object, the object id can never
|
||||
// be assigned to another object. That is unless all captured lols are deleted
|
||||
// which allows the user to start over with a fresh set of lols and object ids.
|
||||
// The uniqueness of the object ids allows the user to track specific objects
|
||||
// and inspect its longevity while debugging JS code in execution.
|
||||
//
|
||||
// The lol comes with utility functions to capture, dump, summarize, and diff
|
||||
// captured lols amongst other functionality. These functionality are
|
||||
// accessible via the v8 debugger interface.
|
||||
class LiveObjectList {
|
||||
public:
|
||||
inline static void GCEpilogue() {}
|
||||
inline static void GCPrologue() {}
|
||||
inline static void IterateElements(ObjectVisitor* v) {}
|
||||
inline static void ProcessNonLive(HeapObject *obj) {}
|
||||
inline static void UpdateReferencesForScavengeGC() {}
|
||||
inline static void GCEpilogue();
|
||||
inline static void GCPrologue();
|
||||
inline static void IterateElements(ObjectVisitor* v);
|
||||
inline static void ProcessNonLive(HeapObject *obj);
|
||||
inline static void UpdateReferencesForScavengeGC();
|
||||
|
||||
static MaybeObject* Capture() { return Heap::undefined_value(); }
|
||||
static bool Delete(int id) { return false; }
|
||||
// Note: LOLs can be listed by calling Dump(0, <lol id>), and 2 LOLs can be
|
||||
// compared/diff'ed using Dump(<lol id1>, <lol id2>, ...). This will yield
|
||||
// a verbose dump of all the objects in the resultant lists.
|
||||
// Similarly, a summarized result of a LOL listing or a diff can be
|
||||
// attained using the Summarize(0, <lol id>) and Summarize(<lol id1,
|
||||
// <lol id2>, ...) respectively.
|
||||
|
||||
static MaybeObject* Capture();
|
||||
static bool Delete(int id);
|
||||
static MaybeObject* Dump(int id1,
|
||||
int id2,
|
||||
int start_idx,
|
||||
int dump_limit,
|
||||
Handle<JSObject> filter_obj) {
|
||||
return Heap::undefined_value();
|
||||
}
|
||||
static MaybeObject* Info(int start_idx, int dump_limit) {
|
||||
return Heap::undefined_value();
|
||||
}
|
||||
static MaybeObject* Summarize(int id1,
|
||||
int id2,
|
||||
Handle<JSObject> filter_obj) {
|
||||
return Heap::undefined_value();
|
||||
}
|
||||
Handle<JSObject> filter_obj);
|
||||
static MaybeObject* Info(int start_idx, int dump_limit);
|
||||
static MaybeObject* Summarize(int id1, int id2, Handle<JSObject> filter_obj);
|
||||
|
||||
static void Reset() {}
|
||||
static Object* GetObj(int obj_id) { return Heap::undefined_value(); }
|
||||
static Object* GetObjId(Handle<String> address) {
|
||||
return Heap::undefined_value();
|
||||
}
|
||||
static void Reset();
|
||||
static Object* GetObj(int obj_id);
|
||||
static int GetObjId(Object* obj);
|
||||
static Object* GetObjId(Handle<String> address);
|
||||
static MaybeObject* GetObjRetainers(int obj_id,
|
||||
Handle<JSObject> instance_filter,
|
||||
bool verbose,
|
||||
int start,
|
||||
int count,
|
||||
Handle<JSObject> filter_obj) {
|
||||
return Heap::undefined_value();
|
||||
}
|
||||
Handle<JSObject> filter_obj);
|
||||
|
||||
static Object* GetPath(int obj_id1,
|
||||
int obj_id2,
|
||||
Handle<JSObject> instance_filter) {
|
||||
return Heap::undefined_value();
|
||||
Handle<JSObject> instance_filter);
|
||||
static Object* PrintObj(int obj_id);
|
||||
|
||||
private:
|
||||
|
||||
struct Element {
|
||||
int id_;
|
||||
HeapObject* obj_;
|
||||
};
|
||||
|
||||
explicit LiveObjectList(LiveObjectList* prev, int capacity);
|
||||
~LiveObjectList();
|
||||
|
||||
static void GCEpiloguePrivate();
|
||||
static void IterateElementsPrivate(ObjectVisitor* v);
|
||||
|
||||
static void DoProcessNonLive(HeapObject *obj);
|
||||
|
||||
static int CompareElement(const Element* a, const Element* b);
|
||||
|
||||
static Object* GetPathPrivate(HeapObject* obj1, HeapObject* obj2);
|
||||
|
||||
static int GetRetainers(Handle<HeapObject> target,
|
||||
Handle<JSObject> instance_filter,
|
||||
Handle<FixedArray> retainers_arr,
|
||||
int start,
|
||||
int dump_limit,
|
||||
int* total_count,
|
||||
LolFilter* filter,
|
||||
LiveObjectSummary *summary,
|
||||
JSFunction* arguments_function,
|
||||
Handle<Object> error);
|
||||
|
||||
static MaybeObject* DumpPrivate(DumpWriter* writer,
|
||||
int start,
|
||||
int dump_limit,
|
||||
LolFilter* filter);
|
||||
static MaybeObject* SummarizePrivate(SummaryWriter* writer,
|
||||
LolFilter* filter,
|
||||
bool is_tracking_roots);
|
||||
|
||||
static bool NeedLOLProcessing() { return (last() != NULL); }
|
||||
static void NullifyNonLivePointer(HeapObject **p) {
|
||||
// Mask out the low bit that marks this as a heap object. We'll use this
|
||||
// cleared bit as an indicator that this pointer needs to be collected.
|
||||
//
|
||||
// Meanwhile, we still preserve its approximate value so that we don't
|
||||
// have to resort the elements list all the time.
|
||||
//
|
||||
// Note: Doing so also makes this HeapObject* look like an SMI. Hence,
|
||||
// GC pointer updater will ignore it when it gets scanned.
|
||||
*p = reinterpret_cast<HeapObject*>((*p)->address());
|
||||
}
|
||||
|
||||
LiveObjectList* prev() { return prev_; }
|
||||
LiveObjectList* next() { return next_; }
|
||||
int id() { return id_; }
|
||||
|
||||
static int list_count() { return list_count_; }
|
||||
static LiveObjectList* last() { return last_; }
|
||||
|
||||
inline static LiveObjectList* FindLolForId(int id, LiveObjectList* start_lol);
|
||||
int TotalObjCount() { return GetTotalObjCountAndSize(NULL); }
|
||||
int GetTotalObjCountAndSize(int* size_p);
|
||||
|
||||
bool Add(HeapObject* obj);
|
||||
Element* Find(HeapObject* obj);
|
||||
static void NullifyMostRecent(HeapObject* obj);
|
||||
void Sort();
|
||||
static void SortAll();
|
||||
|
||||
static void PurgeDuplicates(); // Only to be called by GCEpilogue.
|
||||
|
||||
#ifdef VERIFY_LOL
|
||||
static void Verify(bool match_heap_exactly = false);
|
||||
static void VerifyNotInFromSpace();
|
||||
#endif
|
||||
|
||||
// Iterates the elements in every lol and returns the one that matches the
|
||||
// specified key. If no matching element is found, then it returns NULL.
|
||||
template <typename T>
|
||||
inline static LiveObjectList::Element*
|
||||
FindElementFor(T (*GetValue)(LiveObjectList::Element*), T key);
|
||||
|
||||
inline static int GetElementId(Element* element);
|
||||
inline static HeapObject* GetElementObj(Element* element);
|
||||
|
||||
// Instance fields.
|
||||
LiveObjectList* prev_;
|
||||
LiveObjectList* next_;
|
||||
int id_;
|
||||
int capacity_;
|
||||
int obj_count_;
|
||||
Element *elements_;
|
||||
|
||||
// Statics for managing all the lists.
|
||||
static uint32_t next_element_id_;
|
||||
static int list_count_;
|
||||
static int last_id_;
|
||||
static LiveObjectList* first_;
|
||||
static LiveObjectList* last_;
|
||||
|
||||
friend class LolIterator;
|
||||
friend class LolForwardIterator;
|
||||
friend class LolDumpWriter;
|
||||
friend class RetainersDumpWriter;
|
||||
friend class RetainersSummaryWriter;
|
||||
friend class UpdateLiveObjectListVisitor;
|
||||
};
|
||||
|
||||
|
||||
// Helper class for updating the LiveObjectList HeapObject pointers.
|
||||
class UpdateLiveObjectListVisitor: public ObjectVisitor {
|
||||
public:
|
||||
|
||||
void VisitPointer(Object** p) { UpdatePointer(p); }
|
||||
|
||||
void VisitPointers(Object** start, Object** end) {
|
||||
// Copy all HeapObject pointers in [start, end).
|
||||
for (Object** p = start; p < end; p++) UpdatePointer(p);
|
||||
}
|
||||
|
||||
private:
|
||||
// Based on Heap::ScavengeObject() but only does forwarding of pointers
|
||||
// to live new space objects, and not actually keep them alive.
|
||||
void UpdatePointer(Object** p) {
|
||||
Object* object = *p;
|
||||
if (!Heap::InNewSpace(object)) return;
|
||||
|
||||
HeapObject* heap_obj = HeapObject::cast(object);
|
||||
ASSERT(Heap::InFromSpace(heap_obj));
|
||||
|
||||
// We use the first word (where the map pointer usually is) of a heap
|
||||
// object to record the forwarding pointer. A forwarding pointer can
|
||||
// point to an old space, the code space, or the to space of the new
|
||||
// generation.
|
||||
MapWord first_word = heap_obj->map_word();
|
||||
|
||||
// If the first word is a forwarding address, the object has already been
|
||||
// copied.
|
||||
if (first_word.IsForwardingAddress()) {
|
||||
*p = first_word.ToForwardingAddress();
|
||||
return;
|
||||
|
||||
// Else, it's a dead object.
|
||||
} else {
|
||||
LiveObjectList::NullifyNonLivePointer(reinterpret_cast<HeapObject**>(p));
|
||||
}
|
||||
}
|
||||
static Object* PrintObj(int obj_id) { return Heap::undefined_value(); }
|
||||
};
|
||||
|
||||
|
||||
@ -96,11 +267,50 @@ class LiveObjectList {
|
||||
|
||||
class LiveObjectList {
|
||||
public:
|
||||
static void GCEpilogue() {}
|
||||
static void GCPrologue() {}
|
||||
static void IterateElements(ObjectVisitor* v) {}
|
||||
static void ProcessNonLive(HeapObject *obj) {}
|
||||
static void UpdateReferencesForScavengeGC() {}
|
||||
inline static void GCEpilogue() {}
|
||||
inline static void GCPrologue() {}
|
||||
inline static void IterateElements(ObjectVisitor* v) {}
|
||||
inline static void ProcessNonLive(HeapObject* obj) {}
|
||||
inline static void UpdateReferencesForScavengeGC() {}
|
||||
|
||||
inline static MaybeObject* Capture() { return Heap::undefined_value(); }
|
||||
inline static bool Delete(int id) { return false; }
|
||||
inline static MaybeObject* Dump(int id1,
|
||||
int id2,
|
||||
int start_idx,
|
||||
int dump_limit,
|
||||
Handle<JSObject> filter_obj) {
|
||||
return Heap::undefined_value();
|
||||
}
|
||||
inline static MaybeObject* Info(int start_idx, int dump_limit) {
|
||||
return Heap::undefined_value();
|
||||
}
|
||||
inline static MaybeObject* Summarize(int id1,
|
||||
int id2,
|
||||
Handle<JSObject> filter_obj) {
|
||||
return Heap::undefined_value();
|
||||
}
|
||||
|
||||
inline static void Reset() {}
|
||||
inline static Object* GetObj(int obj_id) { return Heap::undefined_value(); }
|
||||
inline static Object* GetObjId(Handle<String> address) {
|
||||
return Heap::undefined_value();
|
||||
}
|
||||
inline static MaybeObject* GetObjRetainers(int obj_id,
|
||||
Handle<JSObject> instance_filter,
|
||||
bool verbose,
|
||||
int start,
|
||||
int count,
|
||||
Handle<JSObject> filter_obj) {
|
||||
return Heap::undefined_value();
|
||||
}
|
||||
|
||||
inline static Object* GetPath(int obj_id1,
|
||||
int obj_id2,
|
||||
Handle<JSObject> instance_filter) {
|
||||
return Heap::undefined_value();
|
||||
}
|
||||
inline static Object* PrintObj(int obj_id) { return Heap::undefined_value(); }
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user