// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef V8_LIVEOBJECTLIST_H_ #define V8_LIVEOBJECTLIST_H_ #include "v8.h" #include "checks.h" #include "heap.h" #include "objects.h" #include "globals.h" namespace v8 { 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 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(); // Note: LOLs can be listed by calling Dump(0, ), and 2 LOLs can be // compared/diff'ed using Dump(, , ...). 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, ) and Summarize(, ...) respectively. static MaybeObject* Capture(); static bool Delete(int id); static MaybeObject* Dump(int id1, int id2, int start_idx, int dump_limit, Handle filter_obj); static MaybeObject* Info(int start_idx, int dump_limit); static MaybeObject* Summarize(int id1, int id2, Handle filter_obj); static void Reset(); static Object* GetObj(int obj_id); static int GetObjId(Object* obj); static Object* GetObjId(Handle address); static MaybeObject* GetObjRetainers(int obj_id, Handle instance_filter, bool verbose, int start, int count, Handle filter_obj); static Object* GetPath(int obj_id1, int obj_id2, Handle 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 target, Handle instance_filter, Handle retainers_arr, int start, int dump_limit, int* total_count, LolFilter* filter, LiveObjectSummary *summary, JSFunction* arguments_function, Handle 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((*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 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(p)); } } }; #else // !LIVE_OBJECT_LIST 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 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 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 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 address) { return Heap::undefined_value(); } inline static MaybeObject* GetObjRetainers(int obj_id, Handle instance_filter, bool verbose, int start, int count, Handle filter_obj) { return Heap::undefined_value(); } inline static Object* GetPath(int obj_id1, int obj_id2, Handle instance_filter) { return Heap::undefined_value(); } inline static Object* PrintObj(int obj_id) { return Heap::undefined_value(); } }; #endif // LIVE_OBJECT_LIST } } // namespace v8::internal #endif // V8_LIVEOBJECTLIST_H_