• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2011 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 //     * Redistributions of source code must retain the above copyright
7 //       notice, this list of conditions and the following disclaimer.
8 //     * Redistributions in binary form must reproduce the above
9 //       copyright notice, this list of conditions and the following
10 //       disclaimer in the documentation and/or other materials provided
11 //       with the distribution.
12 //     * Neither the name of Google Inc. nor the names of its
13 //       contributors may be used to endorse or promote products derived
14 //       from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 #ifdef LIVE_OBJECT_LIST
29 
30 #include <ctype.h>
31 #include <stdlib.h>
32 
33 #include "v8.h"
34 
35 #include "checks.h"
36 #include "global-handles.h"
37 #include "heap.h"
38 #include "inspector.h"
39 #include "list-inl.h"
40 #include "liveobjectlist-inl.h"
41 #include "string-stream.h"
42 #include "top.h"
43 #include "v8utils.h"
44 
45 namespace v8 {
46 namespace internal {
47 
48 
49 typedef int (*RawComparer)(const void*, const void*);
50 
51 
52 #ifdef CHECK_ALL_OBJECT_TYPES
53 
54 #define DEBUG_LIVE_OBJECT_TYPES(v) \
55   v(Smi, "unexpected: Smi") \
56   \
57   v(CodeCache, "unexpected: CodeCache") \
58   v(BreakPointInfo, "unexpected: BreakPointInfo") \
59   v(DebugInfo, "unexpected: DebugInfo") \
60   v(TypeSwitchInfo, "unexpected: TypeSwitchInfo") \
61   v(SignatureInfo, "unexpected: SignatureInfo") \
62   v(Script, "unexpected: Script") \
63   v(ObjectTemplateInfo, "unexpected: ObjectTemplateInfo") \
64   v(FunctionTemplateInfo, "unexpected: FunctionTemplateInfo") \
65   v(CallHandlerInfo, "unexpected: CallHandlerInfo") \
66   v(InterceptorInfo, "unexpected: InterceptorInfo") \
67   v(AccessCheckInfo, "unexpected: AccessCheckInfo") \
68   v(AccessorInfo, "unexpected: AccessorInfo") \
69   v(ExternalTwoByteString, "unexpected: ExternalTwoByteString") \
70   v(ExternalAsciiString, "unexpected: ExternalAsciiString") \
71   v(ExternalString, "unexpected: ExternalString") \
72   v(SeqTwoByteString, "unexpected: SeqTwoByteString") \
73   v(SeqAsciiString, "unexpected: SeqAsciiString") \
74   v(SeqString, "unexpected: SeqString") \
75   v(JSFunctionResultCache, "unexpected: JSFunctionResultCache") \
76   v(GlobalContext, "unexpected: GlobalContext") \
77   v(MapCache, "unexpected: MapCache") \
78   v(CodeCacheHashTable, "unexpected: CodeCacheHashTable") \
79   v(CompilationCacheTable, "unexpected: CompilationCacheTable") \
80   v(SymbolTable, "unexpected: SymbolTable") \
81   v(Dictionary, "unexpected: Dictionary") \
82   v(HashTable, "unexpected: HashTable") \
83   v(DescriptorArray, "unexpected: DescriptorArray") \
84   v(ExternalFloatArray, "unexpected: ExternalFloatArray") \
85   v(ExternalUnsignedIntArray, "unexpected: ExternalUnsignedIntArray") \
86   v(ExternalIntArray, "unexpected: ExternalIntArray") \
87   v(ExternalUnsignedShortArray, "unexpected: ExternalUnsignedShortArray") \
88   v(ExternalShortArray, "unexpected: ExternalShortArray") \
89   v(ExternalUnsignedByteArray, "unexpected: ExternalUnsignedByteArray") \
90   v(ExternalByteArray, "unexpected: ExternalByteArray") \
91   v(JSValue, "unexpected: JSValue")
92 
93 #else
94 #define DEBUG_LIVE_OBJECT_TYPES(v)
95 #endif
96 
97 
98 #define FOR_EACH_LIVE_OBJECT_TYPE(v) \
99   DEBUG_LIVE_OBJECT_TYPES(v) \
100   \
101   v(JSArray, "JSArray") \
102   v(JSRegExp, "JSRegExp") \
103   v(JSFunction, "JSFunction") \
104   v(JSGlobalObject, "JSGlobal") \
105   v(JSBuiltinsObject, "JSBuiltins") \
106   v(GlobalObject, "Global") \
107   v(JSGlobalProxy, "JSGlobalProxy") \
108   v(JSObject, "JSObject") \
109   \
110   v(Context, "meta: Context") \
111   v(ByteArray, "meta: ByteArray") \
112   v(PixelArray, "meta: PixelArray") \
113   v(ExternalArray, "meta: ExternalArray") \
114   v(FixedArray, "meta: FixedArray") \
115   v(String, "String") \
116   v(HeapNumber, "HeapNumber") \
117   \
118   v(Code, "meta: Code") \
119   v(Map, "meta: Map") \
120   v(Oddball, "Oddball") \
121   v(Proxy, "meta: Proxy") \
122   v(SharedFunctionInfo, "meta: SharedFunctionInfo") \
123   v(Struct, "meta: Struct") \
124   \
125   v(HeapObject, "HeapObject")
126 
127 
128 enum /* LiveObjectType */ {
129 #define DECLARE_OBJECT_TYPE_ENUM(type, name) kType##type,
130   FOR_EACH_LIVE_OBJECT_TYPE(DECLARE_OBJECT_TYPE_ENUM)
131   kInvalidLiveObjType,
132   kNumberOfTypes
133 #undef DECLARE_OBJECT_TYPE_ENUM
134 };
135 
136 
GetObjectType(HeapObject * heap_obj)137 LiveObjectType GetObjectType(HeapObject* heap_obj) {
138   // TODO(mlam): investigate usint Map::instance_type() instead.
139 #define CHECK_FOR_OBJECT_TYPE(type, name) \
140   if (heap_obj->Is##type()) return kType##type;
141   FOR_EACH_LIVE_OBJECT_TYPE(CHECK_FOR_OBJECT_TYPE)
142 #undef CHECK_FOR_OBJECT_TYPE
143 
144   UNREACHABLE();
145   return kInvalidLiveObjType;
146 }
147 
148 
GetObjectTypeDesc(LiveObjectType type)149 inline const char* GetObjectTypeDesc(LiveObjectType type) {
150   static const char* const name[kNumberOfTypes] = {
151   #define DEFINE_OBJECT_TYPE_NAME(type, name) name,
152     FOR_EACH_LIVE_OBJECT_TYPE(DEFINE_OBJECT_TYPE_NAME)
153     "invalid"
154   #undef DEFINE_OBJECT_TYPE_NAME
155   };
156   ASSERT(type < kNumberOfTypes);
157   return name[type];
158 }
159 
160 
GetObjectTypeDesc(HeapObject * heap_obj)161 const char* GetObjectTypeDesc(HeapObject* heap_obj) {
162   LiveObjectType type = GetObjectType(heap_obj);
163   return GetObjectTypeDesc(type);
164 }
165 
166 
IsOfType(LiveObjectType type,HeapObject * obj)167 bool IsOfType(LiveObjectType type, HeapObject *obj) {
168   // Note: there are types that are more general (e.g. JSObject) that would
169   // have passed the Is##type_() test for more specialized types (e.g.
170   // JSFunction).  If we find a more specialized match but we're looking for
171   // the general type, then we should reject the ones that matches the
172   // specialized type.
173 #define CHECK_OBJECT_TYPE(type_, name) \
174   if (obj->Is##type_()) return (type == kType##type_);
175 
176   FOR_EACH_LIVE_OBJECT_TYPE(CHECK_OBJECT_TYPE)
177 #undef CHECK_OBJECT_TYPE
178 
179   return false;
180 }
181 
182 
183 const AllocationSpace kInvalidSpace = static_cast<AllocationSpace>(-1);
184 
FindSpaceFor(String * space_str)185 static AllocationSpace FindSpaceFor(String* space_str) {
186   SmartPointer<char> s =
187       space_str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
188 
189   const char* key_str = *s;
190   switch (key_str[0]) {
191     case 'c':
192       if (strcmp(key_str, "cell") == 0) return CELL_SPACE;
193       if (strcmp(key_str, "code") == 0) return CODE_SPACE;
194       break;
195     case 'l':
196       if (strcmp(key_str, "lo") == 0) return LO_SPACE;
197       break;
198     case 'm':
199       if (strcmp(key_str, "map") == 0) return MAP_SPACE;
200       break;
201     case 'n':
202       if (strcmp(key_str, "new") == 0) return NEW_SPACE;
203       break;
204     case 'o':
205       if (strcmp(key_str, "old-pointer") == 0) return OLD_POINTER_SPACE;
206       if (strcmp(key_str, "old-data") == 0) return OLD_DATA_SPACE;
207       break;
208   }
209   return kInvalidSpace;
210 }
211 
212 
InSpace(AllocationSpace space,HeapObject * heap_obj)213 static bool InSpace(AllocationSpace space, HeapObject *heap_obj) {
214   if (space != LO_SPACE) {
215     return Heap::InSpace(heap_obj, space);
216   }
217 
218   // This is an optimization to speed up the check for an object in the LO
219   // space by exclusion because we know that all object pointers passed in
220   // here are guaranteed to be in the heap.  Hence, it is safe to infer
221   // using an exclusion test.
222   // Note: calling Heap::InSpace(heap_obj, LO_SPACE) is too slow for our
223   // filters.
224   int first_space = static_cast<int>(FIRST_SPACE);
225   int last_space = static_cast<int>(LO_SPACE);
226   for (int sp = first_space; sp < last_space; sp++) {
227     if (Heap::InSpace(heap_obj, static_cast<AllocationSpace>(sp))) {
228       return false;
229     }
230   }
231   SLOW_ASSERT(Heap::InSpace(heap_obj, LO_SPACE));
232   return true;
233 }
234 
235 
FindTypeFor(String * type_str)236 static LiveObjectType FindTypeFor(String* type_str) {
237   SmartPointer<char> s =
238       type_str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
239 
240 #define CHECK_OBJECT_TYPE(type_, name) { \
241     const char* type_desc = GetObjectTypeDesc(kType##type_); \
242     const char* key_str = *s; \
243     if (strstr(type_desc, key_str) != NULL) return kType##type_; \
244   }
245   FOR_EACH_LIVE_OBJECT_TYPE(CHECK_OBJECT_TYPE)
246 #undef CHECK_OBJECT_TYPE
247 
248   return kInvalidLiveObjType;
249 }
250 
251 
252 class LolFilter {
253  public:
254   explicit LolFilter(Handle<JSObject> filter_obj);
255 
is_active() const256   inline bool is_active() const { return is_active_; }
Matches(HeapObject * obj)257   inline bool Matches(HeapObject* obj) {
258     return !is_active() || MatchesSlow(obj);
259   }
260 
261  private:
262   void InitTypeFilter(Handle<JSObject> filter_obj);
263   void InitSpaceFilter(Handle<JSObject> filter_obj);
264   void InitPropertyFilter(Handle<JSObject> filter_obj);
265   bool MatchesSlow(HeapObject* obj);
266 
267   bool is_active_;
268   LiveObjectType type_;
269   AllocationSpace space_;
270   Handle<String> prop_;
271 };
272 
273 
LolFilter(Handle<JSObject> filter_obj)274 LolFilter::LolFilter(Handle<JSObject> filter_obj)
275     : is_active_(false),
276       type_(kInvalidLiveObjType),
277       space_(kInvalidSpace),
278       prop_() {
279   if (filter_obj.is_null()) return;
280 
281   InitTypeFilter(filter_obj);
282   InitSpaceFilter(filter_obj);
283   InitPropertyFilter(filter_obj);
284 }
285 
286 
InitTypeFilter(Handle<JSObject> filter_obj)287 void LolFilter::InitTypeFilter(Handle<JSObject> filter_obj) {
288   Handle<String> type_sym = Factory::LookupAsciiSymbol("type");
289   MaybeObject* maybe_result = filter_obj->GetProperty(*type_sym);
290   Object* type_obj;
291   if (maybe_result->ToObject(&type_obj)) {
292     if (type_obj->IsString()) {
293       String* type_str = String::cast(type_obj);
294       type_ = FindTypeFor(type_str);
295       if (type_ != kInvalidLiveObjType) {
296         is_active_ = true;
297       }
298     }
299   }
300 }
301 
302 
InitSpaceFilter(Handle<JSObject> filter_obj)303 void LolFilter::InitSpaceFilter(Handle<JSObject> filter_obj) {
304   Handle<String> space_sym = Factory::LookupAsciiSymbol("space");
305   MaybeObject* maybe_result = filter_obj->GetProperty(*space_sym);
306   Object* space_obj;
307   if (maybe_result->ToObject(&space_obj)) {
308     if (space_obj->IsString()) {
309       String* space_str = String::cast(space_obj);
310       space_ = FindSpaceFor(space_str);
311       if (space_ != kInvalidSpace) {
312         is_active_ = true;
313       }
314     }
315   }
316 }
317 
318 
InitPropertyFilter(Handle<JSObject> filter_obj)319 void LolFilter::InitPropertyFilter(Handle<JSObject> filter_obj) {
320   Handle<String> prop_sym = Factory::LookupAsciiSymbol("prop");
321   MaybeObject* maybe_result = filter_obj->GetProperty(*prop_sym);
322   Object* prop_obj;
323   if (maybe_result->ToObject(&prop_obj)) {
324     if (prop_obj->IsString()) {
325       prop_ = Handle<String>(String::cast(prop_obj));
326       is_active_ = true;
327     }
328   }
329 }
330 
331 
MatchesSlow(HeapObject * obj)332 bool LolFilter::MatchesSlow(HeapObject* obj) {
333   if ((type_ != kInvalidLiveObjType) && !IsOfType(type_, obj)) {
334     return false;  // Fail because obj is not of the type of interest.
335   }
336   if ((space_ != kInvalidSpace) && !InSpace(space_, obj)) {
337     return false;  // Fail because obj is not in the space of interest.
338   }
339   if (!prop_.is_null() && obj->IsJSObject()) {
340     LookupResult result;
341     obj->Lookup(*prop_, &result);
342     if (!result.IsProperty()) {
343       return false;  // Fail because obj does not have the property of interest.
344     }
345   }
346   return true;
347 }
348 
349 
350 class LolIterator {
351  public:
LolIterator(LiveObjectList * older,LiveObjectList * newer)352   LolIterator(LiveObjectList* older, LiveObjectList* newer)
353       : older_(older),
354         newer_(newer),
355         curr_(0),
356         elements_(0),
357         count_(0),
358         index_(0) { }
359 
Init()360   inline void Init() {
361     SetCurrent(newer_);
362     // If the elements_ list is empty, then move on to the next list as long
363     // as we're not at the last list (indicated by done()).
364     while ((elements_ == NULL) && !Done()) {
365       SetCurrent(curr_->prev_);
366     }
367   }
368 
Done() const369   inline bool Done() const {
370     return (curr_ == older_);
371   }
372 
373   // Object level iteration.
Next()374   inline void Next() {
375     index_++;
376     if (index_ >= count_) {
377       // Iterate backwards until we get to the oldest list.
378       while (!Done()) {
379         SetCurrent(curr_->prev_);
380         // If we have elements to process, we're good to go.
381         if (elements_ != NULL) break;
382 
383         // Else, we should advance to the next older list.
384       }
385     }
386   }
387 
Id() const388   inline int Id() const {
389     return elements_[index_].id_;
390   }
Obj() const391   inline HeapObject* Obj() const {
392     return elements_[index_].obj_;
393   }
394 
LolObjCount() const395   inline int LolObjCount() const {
396     if (curr_ != NULL) return curr_->obj_count_;
397     return 0;
398   }
399 
400  protected:
SetCurrent(LiveObjectList * new_curr)401   inline void SetCurrent(LiveObjectList* new_curr) {
402     curr_ = new_curr;
403     if (curr_ != NULL) {
404       elements_ = curr_->elements_;
405       count_ = curr_->obj_count_;
406       index_ = 0;
407     }
408   }
409 
410   LiveObjectList* older_;
411   LiveObjectList* newer_;
412   LiveObjectList* curr_;
413   LiveObjectList::Element* elements_;
414   int count_;
415   int index_;
416 };
417 
418 
419 class LolForwardIterator : public LolIterator {
420  public:
LolForwardIterator(LiveObjectList * first,LiveObjectList * last)421   LolForwardIterator(LiveObjectList* first, LiveObjectList* last)
422       : LolIterator(first, last) {
423   }
424 
Init()425   inline void Init() {
426     SetCurrent(older_);
427     // If the elements_ list is empty, then move on to the next list as long
428     // as we're not at the last list (indicated by Done()).
429     while ((elements_ == NULL) && !Done()) {
430       SetCurrent(curr_->next_);
431     }
432   }
433 
Done() const434   inline bool Done() const {
435     return (curr_ == newer_);
436   }
437 
438   // Object level iteration.
Next()439   inline void Next() {
440     index_++;
441     if (index_ >= count_) {
442       // Done with current list.  Move on to the next.
443       while (!Done()) {  // If not at the last list already, ...
444         SetCurrent(curr_->next_);
445         // If we have elements to process, we're good to go.
446         if (elements_ != NULL) break;
447 
448         // Else, we should advance to the next list.
449       }
450     }
451   }
452 };
453 
454 
455 // Minimizes the white space in a string.  Tabs and newlines are replaced
456 // with a space where appropriate.
CompactString(char * str)457 static int CompactString(char* str) {
458   char* src = str;
459   char* dst = str;
460   char prev_ch = 0;
461   while (*dst != '\0') {
462     char ch = *src++;
463     // We will treat non-ascii chars as '?'.
464     if ((ch & 0x80) != 0) {
465       ch = '?';
466     }
467     // Compact contiguous whitespace chars into a single ' '.
468     if (isspace(ch)) {
469       if (prev_ch != ' ') *dst++ = ' ';
470       prev_ch = ' ';
471       continue;
472     }
473     *dst++ = ch;
474     prev_ch = ch;
475   }
476   return (dst - str);
477 }
478 
479 
480 // Generates a custom description based on the specific type of
481 // object we're looking at.  We only generate specialized
482 // descriptions where we can.  In all other cases, we emit the
483 // generic info.
GenerateObjectDesc(HeapObject * obj,char * buffer,int buffer_size)484 static void GenerateObjectDesc(HeapObject* obj,
485                                char* buffer,
486                                int buffer_size) {
487   Vector<char> buffer_v(buffer, buffer_size);
488   ASSERT(obj != NULL);
489   if (obj->IsJSArray()) {
490     JSArray* jsarray = JSArray::cast(obj);
491     double length = jsarray->length()->Number();
492     OS::SNPrintF(buffer_v,
493                  "%p <%s> len %g",
494                  reinterpret_cast<void*>(obj),
495                  GetObjectTypeDesc(obj),
496                  length);
497 
498   } else if (obj->IsString()) {
499     String *str = String::cast(obj);
500     // Only grab up to 160 chars in case they are double byte.
501     // We'll only dump 80 of them after we compact them.
502     const int kMaxCharToDump = 80;
503     const int kMaxBufferSize = kMaxCharToDump * 2;
504     SmartPointer<char> str_sp = str->ToCString(DISALLOW_NULLS,
505                                                ROBUST_STRING_TRAVERSAL,
506                                                0,
507                                                kMaxBufferSize);
508     char* str_cstr = *str_sp;
509     int length = CompactString(str_cstr);
510     OS::SNPrintF(buffer_v,
511                  "%p <%s> '%.80s%s'",
512                  reinterpret_cast<void*>(obj),
513                  GetObjectTypeDesc(obj),
514                  str_cstr,
515                  (length > kMaxCharToDump) ? "..." : "");
516 
517   } else if (obj->IsJSFunction() || obj->IsSharedFunctionInfo()) {
518     SharedFunctionInfo* sinfo;
519     if (obj->IsJSFunction()) {
520       JSFunction* func = JSFunction::cast(obj);
521       sinfo = func->shared();
522     } else {
523       sinfo = SharedFunctionInfo::cast(obj);
524     }
525 
526     String* name = sinfo->DebugName();
527     SmartPointer<char> name_sp =
528         name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
529     char* name_cstr = *name_sp;
530 
531     HeapStringAllocator string_allocator;
532     StringStream stream(&string_allocator);
533     sinfo->SourceCodePrint(&stream, 50);
534     SmartPointer<const char> source_sp = stream.ToCString();
535     const char* source_cstr = *source_sp;
536 
537     OS::SNPrintF(buffer_v,
538                  "%p <%s> '%s' %s",
539                  reinterpret_cast<void*>(obj),
540                  GetObjectTypeDesc(obj),
541                  name_cstr,
542                  source_cstr);
543 
544   } else if (obj->IsFixedArray()) {
545     FixedArray* fixed = FixedArray::cast(obj);
546 
547     OS::SNPrintF(buffer_v,
548                  "%p <%s> len %d",
549                  reinterpret_cast<void*>(obj),
550                  GetObjectTypeDesc(obj),
551                  fixed->length());
552 
553   } else {
554     OS::SNPrintF(buffer_v,
555                  "%p <%s>",
556                  reinterpret_cast<void*>(obj),
557                  GetObjectTypeDesc(obj));
558   }
559 }
560 
561 
562 // Utility function for filling in a line of detail in a verbose dump.
AddObjDetail(Handle<FixedArray> arr,int index,int obj_id,Handle<HeapObject> target,const char * desc_str,Handle<String> id_sym,Handle<String> desc_sym,Handle<String> size_sym,Handle<JSObject> detail,Handle<String> desc,Handle<Object> error)563 static bool AddObjDetail(Handle<FixedArray> arr,
564                          int index,
565                          int obj_id,
566                          Handle<HeapObject> target,
567                          const char* desc_str,
568                          Handle<String> id_sym,
569                          Handle<String> desc_sym,
570                          Handle<String> size_sym,
571                          Handle<JSObject> detail,
572                          Handle<String> desc,
573                          Handle<Object> error) {
574   detail = Factory::NewJSObject(Top::object_function());
575   if (detail->IsFailure()) {
576     error = detail;
577     return false;
578   }
579 
580   int size = 0;
581   char buffer[512];
582   if (desc_str == NULL) {
583     ASSERT(!target.is_null());
584     HeapObject* obj = *target;
585     GenerateObjectDesc(obj, buffer, sizeof(buffer));
586     desc_str = buffer;
587     size = obj->Size();
588   }
589   desc = Factory::NewStringFromAscii(CStrVector(desc_str));
590   if (desc->IsFailure()) {
591     error = desc;
592     return false;
593   }
594 
595   { MaybeObject* maybe_result = detail->SetProperty(*id_sym,
596                                                     Smi::FromInt(obj_id),
597                                                     NONE,
598                                                     kNonStrictMode);
599     if (maybe_result->IsFailure()) return false;
600   }
601   { MaybeObject* maybe_result = detail->SetProperty(*desc_sym,
602                                                     *desc,
603                                                     NONE,
604                                                     kNonStrictMode);
605     if (maybe_result->IsFailure()) return false;
606   }
607   { MaybeObject* maybe_result = detail->SetProperty(*size_sym,
608                                                     Smi::FromInt(size),
609                                                     NONE,
610                                                     kNonStrictMode);
611     if (maybe_result->IsFailure()) return false;
612   }
613 
614   arr->set(index, *detail);
615   return true;
616 }
617 
618 
619 class DumpWriter {
620  public:
~DumpWriter()621   virtual ~DumpWriter() {}
622 
623   virtual void ComputeTotalCountAndSize(LolFilter* filter,
624                                         int* count,
625                                         int* size) = 0;
626   virtual bool Write(Handle<FixedArray> elements_arr,
627                      int start,
628                      int dump_limit,
629                      LolFilter* filter,
630                      Handle<Object> error) = 0;
631 };
632 
633 
634 class LolDumpWriter: public DumpWriter {
635  public:
LolDumpWriter(LiveObjectList * older,LiveObjectList * newer)636   LolDumpWriter(LiveObjectList* older, LiveObjectList* newer)
637       : older_(older), newer_(newer) {
638   }
639 
ComputeTotalCountAndSize(LolFilter * filter,int * count,int * size)640   void ComputeTotalCountAndSize(LolFilter* filter, int* count, int* size) {
641     *count = 0;
642     *size = 0;
643 
644     LolIterator it(older_, newer_);
645     for (it.Init(); !it.Done(); it.Next()) {
646       HeapObject* heap_obj = it.Obj();
647       if (!filter->Matches(heap_obj)) {
648         continue;
649       }
650 
651       *size += heap_obj->Size();
652       (*count)++;
653     }
654   }
655 
Write(Handle<FixedArray> elements_arr,int start,int dump_limit,LolFilter * filter,Handle<Object> error)656   bool Write(Handle<FixedArray> elements_arr,
657              int start,
658              int dump_limit,
659              LolFilter* filter,
660              Handle<Object> error) {
661     // The lols are listed in latest to earliest.  We want to dump from
662     // earliest to latest.  So, compute the last element to start with.
663     int index = 0;
664     int count = 0;
665 
666     // Prefetch some needed symbols.
667     Handle<String> id_sym = Factory::LookupAsciiSymbol("id");
668     Handle<String> desc_sym = Factory::LookupAsciiSymbol("desc");
669     Handle<String> size_sym = Factory::LookupAsciiSymbol("size");
670 
671     // Fill the array with the lol object details.
672     Handle<JSObject> detail;
673     Handle<String> desc;
674     Handle<HeapObject> target;
675 
676     LiveObjectList* first_lol = (older_ != NULL) ?
677                                 older_->next_ : LiveObjectList::first_;
678     LiveObjectList* last_lol = (newer_ != NULL) ? newer_->next_ : NULL;
679 
680     LolForwardIterator it(first_lol, last_lol);
681     for (it.Init(); !it.Done() && (index < dump_limit); it.Next()) {
682       HeapObject* heap_obj = it.Obj();
683 
684       // Skip objects that have been filtered out.
685       if (!filter->Matches(heap_obj)) {
686         continue;
687       }
688 
689       // Only report objects that are in the section of interest.
690       if (count >= start) {
691         target = Handle<HeapObject>(heap_obj);
692         bool success = AddObjDetail(elements_arr,
693                                     index++,
694                                     it.Id(),
695                                     target,
696                                     NULL,
697                                     id_sym,
698                                     desc_sym,
699                                     size_sym,
700                                     detail,
701                                     desc,
702                                     error);
703         if (!success) return false;
704       }
705       count++;
706     }
707     return true;
708   }
709 
710  private:
711   LiveObjectList* older_;
712   LiveObjectList* newer_;
713 };
714 
715 
716 class RetainersDumpWriter: public DumpWriter {
717  public:
RetainersDumpWriter(Handle<HeapObject> target,Handle<JSObject> instance_filter,Handle<JSFunction> args_function)718   RetainersDumpWriter(Handle<HeapObject> target,
719                       Handle<JSObject> instance_filter,
720                       Handle<JSFunction> args_function)
721       : target_(target),
722         instance_filter_(instance_filter),
723         args_function_(args_function) {
724   }
725 
ComputeTotalCountAndSize(LolFilter * filter,int * count,int * size)726   void ComputeTotalCountAndSize(LolFilter* filter, int* count, int* size) {
727     Handle<FixedArray> retainers_arr;
728     Handle<Object> error;
729 
730     *size = -1;
731     LiveObjectList::GetRetainers(target_,
732                                  instance_filter_,
733                                  retainers_arr,
734                                  0,
735                                  Smi::kMaxValue,
736                                  count,
737                                  filter,
738                                  NULL,
739                                  *args_function_,
740                                  error);
741   }
742 
Write(Handle<FixedArray> elements_arr,int start,int dump_limit,LolFilter * filter,Handle<Object> error)743   bool Write(Handle<FixedArray> elements_arr,
744              int start,
745              int dump_limit,
746              LolFilter* filter,
747              Handle<Object> error) {
748     int dummy;
749     int count;
750 
751     // Fill the retainer objects.
752     count = LiveObjectList::GetRetainers(target_,
753                                          instance_filter_,
754                                          elements_arr,
755                                          start,
756                                          dump_limit,
757                                          &dummy,
758                                          filter,
759                                          NULL,
760                                          *args_function_,
761                                          error);
762     if (count < 0) {
763         return false;
764     }
765     return true;
766   }
767 
768  private:
769   Handle<HeapObject> target_;
770   Handle<JSObject> instance_filter_;
771   Handle<JSFunction> args_function_;
772 };
773 
774 
775 class LiveObjectSummary {
776  public:
LiveObjectSummary(LolFilter * filter)777   explicit LiveObjectSummary(LolFilter* filter)
778       : total_count_(0),
779         total_size_(0),
780         found_root_(false),
781         found_weak_root_(false),
782         filter_(filter) {
783     memset(counts_, 0, sizeof(counts_[0]) * kNumberOfEntries);
784     memset(sizes_, 0, sizeof(sizes_[0]) * kNumberOfEntries);
785   }
786 
Add(HeapObject * heap_obj)787   void Add(HeapObject* heap_obj) {
788     int size = heap_obj->Size();
789     LiveObjectType type = GetObjectType(heap_obj);
790     ASSERT(type != kInvalidLiveObjType);
791     counts_[type]++;
792     sizes_[type] += size;
793     total_count_++;
794     total_size_ += size;
795   }
796 
set_found_root()797   void set_found_root() { found_root_ = true; }
set_found_weak_root()798   void set_found_weak_root() { found_weak_root_ = true; }
799 
Count(LiveObjectType type)800   inline int Count(LiveObjectType type) {
801     return counts_[type];
802   }
Size(LiveObjectType type)803   inline int Size(LiveObjectType type) {
804     return sizes_[type];
805   }
total_count()806   inline int total_count() {
807     return total_count_;
808   }
total_size()809   inline int total_size() {
810     return total_size_;
811   }
found_root()812   inline bool found_root() {
813     return found_root_;
814   }
found_weak_root()815   inline bool found_weak_root() {
816     return found_weak_root_;
817   }
GetNumberOfEntries()818   int GetNumberOfEntries() {
819     int entries = 0;
820     for (int i = 0; i < kNumberOfEntries; i++) {
821       if (counts_[i]) entries++;
822     }
823     return entries;
824   }
825 
filter()826   inline LolFilter* filter() { return filter_; }
827 
828   static const int kNumberOfEntries = kNumberOfTypes;
829 
830  private:
831   int counts_[kNumberOfEntries];
832   int sizes_[kNumberOfEntries];
833   int total_count_;
834   int total_size_;
835   bool found_root_;
836   bool found_weak_root_;
837 
838   LolFilter *filter_;
839 };
840 
841 
842 // Abstraction for a summary writer.
843 class SummaryWriter {
844  public:
~SummaryWriter()845   virtual ~SummaryWriter() {}
846   virtual void Write(LiveObjectSummary* summary) = 0;
847 };
848 
849 
850 // A summary writer for filling in a summary of lol lists and diffs.
851 class LolSummaryWriter: public SummaryWriter {
852  public:
LolSummaryWriter(LiveObjectList * older_lol,LiveObjectList * newer_lol)853   LolSummaryWriter(LiveObjectList *older_lol,
854                    LiveObjectList *newer_lol)
855       : older_(older_lol), newer_(newer_lol) {
856   }
857 
Write(LiveObjectSummary * summary)858   void Write(LiveObjectSummary* summary) {
859     LolFilter* filter = summary->filter();
860 
861     // Fill the summary with the lol object details.
862     LolIterator it(older_, newer_);
863     for (it.Init(); !it.Done(); it.Next()) {
864       HeapObject* heap_obj = it.Obj();
865       if (!filter->Matches(heap_obj)) {
866         continue;
867       }
868       summary->Add(heap_obj);
869     }
870   }
871 
872  private:
873   LiveObjectList* older_;
874   LiveObjectList* newer_;
875 };
876 
877 
878 // A summary writer for filling in a retainers list.
879 class RetainersSummaryWriter: public SummaryWriter {
880  public:
RetainersSummaryWriter(Handle<HeapObject> target,Handle<JSObject> instance_filter,Handle<JSFunction> args_function)881   RetainersSummaryWriter(Handle<HeapObject> target,
882                          Handle<JSObject> instance_filter,
883                          Handle<JSFunction> args_function)
884       : target_(target),
885         instance_filter_(instance_filter),
886         args_function_(args_function) {
887   }
888 
Write(LiveObjectSummary * summary)889   void Write(LiveObjectSummary* summary) {
890     Handle<FixedArray> retainers_arr;
891     Handle<Object> error;
892     int dummy_total_count;
893     LiveObjectList::GetRetainers(target_,
894                                  instance_filter_,
895                                  retainers_arr,
896                                  0,
897                                  Smi::kMaxValue,
898                                  &dummy_total_count,
899                                  summary->filter(),
900                                  summary,
901                                  *args_function_,
902                                  error);
903   }
904 
905  private:
906   Handle<HeapObject> target_;
907   Handle<JSObject> instance_filter_;
908   Handle<JSFunction> args_function_;
909 };
910 
911 
912 uint32_t LiveObjectList::next_element_id_ = 1;
913 int LiveObjectList::list_count_ = 0;
914 int LiveObjectList::last_id_ = 0;
915 LiveObjectList* LiveObjectList::first_ = NULL;
916 LiveObjectList* LiveObjectList::last_ = NULL;
917 
918 
LiveObjectList(LiveObjectList * prev,int capacity)919 LiveObjectList::LiveObjectList(LiveObjectList* prev, int capacity)
920     : prev_(prev),
921       next_(NULL),
922       capacity_(capacity),
923       obj_count_(0) {
924   elements_ = NewArray<Element>(capacity);
925   id_ = ++last_id_;
926 
927   list_count_++;
928 }
929 
930 
~LiveObjectList()931 LiveObjectList::~LiveObjectList() {
932   DeleteArray<Element>(elements_);
933   delete prev_;
934 }
935 
936 
GetTotalObjCountAndSize(int * size_p)937 int LiveObjectList::GetTotalObjCountAndSize(int* size_p) {
938   int size = 0;
939   int count = 0;
940   LiveObjectList *lol = this;
941   do {
942     // Only compute total size if requested i.e. when size_p is not null.
943     if (size_p != NULL) {
944       Element* elements = lol->elements_;
945       for (int i = 0; i < lol->obj_count_; i++) {
946         HeapObject* heap_obj = elements[i].obj_;
947         size += heap_obj->Size();
948       }
949     }
950     count += lol->obj_count_;
951     lol = lol->prev_;
952   } while (lol != NULL);
953 
954   if (size_p != NULL) {
955     *size_p = size;
956   }
957   return count;
958 }
959 
960 
961 // Adds an object to the lol.
962 // Returns true if successful, else returns false.
Add(HeapObject * obj)963 bool LiveObjectList::Add(HeapObject* obj) {
964   // If the object is already accounted for in the prev list which we inherit
965   // from, then no need to add it to this list.
966   if ((prev() != NULL) && (prev()->Find(obj) != NULL)) {
967     return true;
968   }
969   ASSERT(obj_count_ <= capacity_);
970   if (obj_count_ == capacity_) {
971     // The heap must have grown and we have more objects than capacity to store
972     // them.
973     return false;  // Fail this addition.
974   }
975   Element& element = elements_[obj_count_++];
976   element.id_ = next_element_id_++;
977   element.obj_ = obj;
978   return true;
979 }
980 
981 
982 // Comparator used for sorting and searching the lol.
CompareElement(const Element * a,const Element * b)983 int LiveObjectList::CompareElement(const Element* a, const Element* b) {
984   const HeapObject* obj1 = a->obj_;
985   const HeapObject* obj2 = b->obj_;
986   // For lol elements, it doesn't matter which comes first if 2 elements point
987   // to the same object (which gets culled later).  Hence, we only care about
988   // the the greater than / less than relationships.
989   return (obj1 > obj2) ? 1 : (obj1 == obj2) ? 0 : -1;
990 }
991 
992 
993 // Looks for the specified object in the lol, and returns its element if found.
Find(HeapObject * obj)994 LiveObjectList::Element* LiveObjectList::Find(HeapObject* obj) {
995   LiveObjectList* lol = this;
996   Element key;
997   Element* result = NULL;
998 
999   key.obj_ = obj;
1000   // Iterate through the chain of lol's to look for the object.
1001   while ((result == NULL) && (lol != NULL)) {
1002     result = reinterpret_cast<Element*>(
1003         bsearch(&key, lol->elements_, lol->obj_count_,
1004                 sizeof(Element),
1005                 reinterpret_cast<RawComparer>(CompareElement)));
1006     lol = lol->prev_;
1007   }
1008   return result;
1009 }
1010 
1011 
1012 // "Nullifies" (convert the HeapObject* into an SMI) so that it will get cleaned
1013 // up in the GCEpilogue, while preserving the sort order of the lol.
1014 // NOTE: the lols need to be already sorted before NullifyMostRecent() is
1015 // called.
NullifyMostRecent(HeapObject * obj)1016 void LiveObjectList::NullifyMostRecent(HeapObject* obj) {
1017   LiveObjectList* lol = last();
1018   Element key;
1019   Element* result = NULL;
1020 
1021   key.obj_ = obj;
1022   // Iterate through the chain of lol's to look for the object.
1023   while (lol != NULL) {
1024     result = reinterpret_cast<Element*>(
1025         bsearch(&key, lol->elements_, lol->obj_count_,
1026                 sizeof(Element),
1027                 reinterpret_cast<RawComparer>(CompareElement)));
1028     if (result != NULL) {
1029       // Since there may be more than one (we are nullifying dup's after all),
1030       // find the first in the current lol, and nullify that.  The lol should
1031       // be sorted already to make this easy (see the use of SortAll()).
1032       int i = result - lol->elements_;
1033 
1034       // NOTE: we sort the lol in increasing order.  So, if an object has been
1035       // "nullified" (its lowest bit will be cleared to make it look like an
1036       // SMI), it would/should show up before the equivalent dups that have not
1037       // yet been "nullified".  Hence, we should be searching backwards for the
1038       // first occurence of a matching object and nullify that instance.  This
1039       // will ensure that we preserve the expected sorting order.
1040       for (i--; i > 0; i--) {
1041         Element* element = &lol->elements_[i];
1042         HeapObject* curr_obj = element->obj_;
1043         if (curr_obj != obj) {
1044             break;  // No more matches.  Let's move on.
1045         }
1046         result = element;  // Let this earlier match be the result.
1047       }
1048 
1049       // Nullify the object.
1050       NullifyNonLivePointer(&result->obj_);
1051       return;
1052     }
1053     lol = lol->prev_;
1054   }
1055 }
1056 
1057 
1058 // Sorts the lol.
Sort()1059 void LiveObjectList::Sort() {
1060   if (obj_count_ > 0) {
1061     Vector<Element> elements_v(elements_, obj_count_);
1062     elements_v.Sort(CompareElement);
1063   }
1064 }
1065 
1066 
1067 // Sorts all captured lols starting from the latest.
SortAll()1068 void LiveObjectList::SortAll() {
1069   LiveObjectList* lol = last();
1070   while (lol != NULL) {
1071     lol->Sort();
1072     lol = lol->prev_;
1073   }
1074 }
1075 
1076 
1077 // Counts the number of objects in the heap.
CountHeapObjects()1078 static int CountHeapObjects() {
1079   int count = 0;
1080   // Iterate over all the heap spaces and count the number of objects.
1081   HeapIterator iterator(HeapIterator::kFilterFreeListNodes);
1082   HeapObject* heap_obj = NULL;
1083   while ((heap_obj = iterator.next()) != NULL) {
1084     count++;
1085   }
1086   return count;
1087 }
1088 
1089 
1090 // Captures a current snapshot of all objects in the heap.
Capture()1091 MaybeObject* LiveObjectList::Capture() {
1092   HandleScope scope;
1093 
1094   // Count the number of objects in the heap.
1095   int total_count = CountHeapObjects();
1096   int count = total_count;
1097   int size = 0;
1098 
1099   LiveObjectList* last_lol = last();
1100   if (last_lol != NULL) {
1101     count -= last_lol->TotalObjCount();
1102   }
1103 
1104   LiveObjectList* lol;
1105 
1106   // Create a lol large enough to track all the objects.
1107   lol = new LiveObjectList(last_lol, count);
1108   if (lol == NULL) {
1109     return NULL;  // No memory to proceed.
1110   }
1111 
1112   // The HeapIterator needs to be in its own scope because it disables
1113   // allocation, and we need allocate below.
1114   {
1115     // Iterate over all the heap spaces and add the objects.
1116     HeapIterator iterator(HeapIterator::kFilterFreeListNodes);
1117     HeapObject* heap_obj = NULL;
1118     bool failed = false;
1119     while (!failed && (heap_obj = iterator.next()) != NULL) {
1120       failed = !lol->Add(heap_obj);
1121       size += heap_obj->Size();
1122     }
1123     ASSERT(!failed);
1124 
1125     lol->Sort();
1126 
1127     // Add the current lol to the list of lols.
1128     if (last_ != NULL) {
1129       last_->next_ = lol;
1130     } else {
1131       first_ = lol;
1132     }
1133     last_ = lol;
1134 
1135 #ifdef VERIFY_LOL
1136     if (FLAG_verify_lol) {
1137       Verify(true);
1138     }
1139 #endif
1140   }
1141 
1142   Handle<String> id_sym = Factory::LookupAsciiSymbol("id");
1143   Handle<String> count_sym = Factory::LookupAsciiSymbol("count");
1144   Handle<String> size_sym = Factory::LookupAsciiSymbol("size");
1145 
1146   Handle<JSObject> result = Factory::NewJSObject(Top::object_function());
1147   if (result->IsFailure()) return Object::cast(*result);
1148 
1149   { MaybeObject* maybe_result = result->SetProperty(*id_sym,
1150                                                     Smi::FromInt(lol->id()),
1151                                                     NONE,
1152                                                     kNonStrictMode);
1153     if (maybe_result->IsFailure()) return maybe_result;
1154   }
1155   { MaybeObject* maybe_result = result->SetProperty(*count_sym,
1156                                                     Smi::FromInt(total_count),
1157                                                     NONE,
1158                                                     kNonStrictMode);
1159     if (maybe_result->IsFailure()) return maybe_result;
1160   }
1161   { MaybeObject* maybe_result = result->SetProperty(*size_sym,
1162                                                     Smi::FromInt(size),
1163                                                     NONE,
1164                                                     kNonStrictMode);
1165     if (maybe_result->IsFailure()) return maybe_result;
1166   }
1167 
1168   return *result;
1169 }
1170 
1171 
1172 // Delete doesn't actually deletes an lol.  It just marks it as invisible since
1173 // its contents are considered to be part of subsequent lists as well.  The
1174 // only time we'll actually delete the lol is when we Reset() or if the lol is
1175 // invisible, and its element count reaches 0.
Delete(int id)1176 bool LiveObjectList::Delete(int id) {
1177   LiveObjectList *lol = last();
1178   while (lol != NULL) {
1179     if (lol->id() == id) {
1180       break;
1181     }
1182     lol = lol->prev_;
1183   }
1184 
1185   // If no lol is found for this id, then we fail to delete.
1186   if (lol == NULL) return false;
1187 
1188   // Else, mark the lol as invisible i.e. id == 0.
1189   lol->id_ = 0;
1190   list_count_--;
1191   ASSERT(list_count_ >= 0);
1192   if (lol->obj_count_ == 0) {
1193     // Point the next lol's prev to this lol's prev.
1194     LiveObjectList* next = lol->next_;
1195     LiveObjectList* prev = lol->prev_;
1196     // Point next's prev to prev.
1197     if (next != NULL) {
1198       next->prev_ = lol->prev_;
1199     } else {
1200       last_ = lol->prev_;
1201     }
1202     // Point prev's next to next.
1203     if (prev != NULL) {
1204       prev->next_ = lol->next_;
1205     } else {
1206       first_ = lol->next_;
1207     }
1208 
1209     lol->prev_ = NULL;
1210     lol->next_ = NULL;
1211 
1212     // Delete this now empty and invisible lol.
1213     delete lol;
1214   }
1215 
1216   // Just in case we've marked everything invisible, then clean up completely.
1217   if (list_count_ == 0) {
1218     Reset();
1219   }
1220 
1221   return true;
1222 }
1223 
1224 
Dump(int older_id,int newer_id,int start_idx,int dump_limit,Handle<JSObject> filter_obj)1225 MaybeObject* LiveObjectList::Dump(int older_id,
1226                                   int newer_id,
1227                                   int start_idx,
1228                                   int dump_limit,
1229                                   Handle<JSObject> filter_obj) {
1230   if ((older_id < 0) || (newer_id < 0) || (last() == NULL)) {
1231     return Failure::Exception();  // Fail: 0 is not a valid lol id.
1232   }
1233   if (newer_id < older_id) {
1234     // They are not in the expected order.  Swap them.
1235     int temp = older_id;
1236     older_id = newer_id;
1237     newer_id = temp;
1238   }
1239 
1240   LiveObjectList *newer_lol = FindLolForId(newer_id, last());
1241   LiveObjectList *older_lol = FindLolForId(older_id, newer_lol);
1242 
1243   // If the id is defined, and we can't find a LOL for it, then we have an
1244   // invalid id.
1245   if ((newer_id != 0) && (newer_lol == NULL)) {
1246     return Failure::Exception();  // Fail: the newer lol id is invalid.
1247   }
1248   if ((older_id != 0) && (older_lol == NULL)) {
1249     return Failure::Exception();  // Fail: the older lol id is invalid.
1250   }
1251 
1252   LolFilter filter(filter_obj);
1253   LolDumpWriter writer(older_lol, newer_lol);
1254   return DumpPrivate(&writer, start_idx, dump_limit, &filter);
1255 }
1256 
1257 
DumpPrivate(DumpWriter * writer,int start,int dump_limit,LolFilter * filter)1258 MaybeObject* LiveObjectList::DumpPrivate(DumpWriter* writer,
1259                                          int start,
1260                                          int dump_limit,
1261                                          LolFilter* filter) {
1262   HandleScope scope;
1263 
1264   // Calculate the number of entries of the dump.
1265   int count = -1;
1266   int size = -1;
1267   writer->ComputeTotalCountAndSize(filter, &count, &size);
1268 
1269   // Adjust for where to start the dump.
1270   if ((start < 0) || (start >= count)) {
1271     return Failure::Exception();  // invalid start.
1272   }
1273 
1274   int remaining_count = count - start;
1275   if (dump_limit > remaining_count) {
1276     dump_limit = remaining_count;
1277   }
1278 
1279   // Allocate an array to hold the result.
1280   Handle<FixedArray> elements_arr = Factory::NewFixedArray(dump_limit);
1281   if (elements_arr->IsFailure()) return Object::cast(*elements_arr);
1282 
1283   // Fill in the dump.
1284   Handle<Object> error;
1285   bool success = writer->Write(elements_arr,
1286                                start,
1287                                dump_limit,
1288                                filter,
1289                                error);
1290   if (!success) return Object::cast(*error);
1291 
1292   MaybeObject* maybe_result;
1293 
1294   // Allocate the result body.
1295   Handle<JSObject> body = Factory::NewJSObject(Top::object_function());
1296   if (body->IsFailure()) return Object::cast(*body);
1297 
1298   // Set the updated body.count.
1299   Handle<String> count_sym = Factory::LookupAsciiSymbol("count");
1300   maybe_result = body->SetProperty(*count_sym,
1301                                    Smi::FromInt(count),
1302                                    NONE,
1303                                    kNonStrictMode);
1304   if (maybe_result->IsFailure()) return maybe_result;
1305 
1306   // Set the updated body.size if appropriate.
1307   if (size >= 0) {
1308     Handle<String> size_sym = Factory::LookupAsciiSymbol("size");
1309     maybe_result = body->SetProperty(*size_sym,
1310                                      Smi::FromInt(size),
1311                                      NONE,
1312                                      kNonStrictMode);
1313     if (maybe_result->IsFailure()) return maybe_result;
1314   }
1315 
1316   // Set body.first_index.
1317   Handle<String> first_sym = Factory::LookupAsciiSymbol("first_index");
1318   maybe_result = body->SetProperty(*first_sym,
1319                                    Smi::FromInt(start),
1320                                    NONE,
1321                                    kNonStrictMode);
1322   if (maybe_result->IsFailure()) return maybe_result;
1323 
1324   // Allocate the JSArray of the elements.
1325   Handle<JSObject> elements = Factory::NewJSObject(Top::array_function());
1326   if (elements->IsFailure()) return Object::cast(*elements);
1327   Handle<JSArray>::cast(elements)->SetContent(*elements_arr);
1328 
1329   // Set body.elements.
1330   Handle<String> elements_sym = Factory::LookupAsciiSymbol("elements");
1331   maybe_result = body->SetProperty(*elements_sym,
1332                                    *elements,
1333                                    NONE,
1334                                    kNonStrictMode);
1335   if (maybe_result->IsFailure()) return maybe_result;
1336 
1337   return *body;
1338 }
1339 
1340 
Summarize(int older_id,int newer_id,Handle<JSObject> filter_obj)1341 MaybeObject* LiveObjectList::Summarize(int older_id,
1342                                        int newer_id,
1343                                        Handle<JSObject> filter_obj) {
1344   if ((older_id < 0) || (newer_id < 0) || (last() == NULL)) {
1345     return Failure::Exception();  // Fail: 0 is not a valid lol id.
1346   }
1347   if (newer_id < older_id) {
1348     // They are not in the expected order.  Swap them.
1349     int temp = older_id;
1350     older_id = newer_id;
1351     newer_id = temp;
1352   }
1353 
1354   LiveObjectList *newer_lol = FindLolForId(newer_id, last());
1355   LiveObjectList *older_lol = FindLolForId(older_id, newer_lol);
1356 
1357   // If the id is defined, and we can't find a LOL for it, then we have an
1358   // invalid id.
1359   if ((newer_id != 0) && (newer_lol == NULL)) {
1360     return Failure::Exception();  // Fail: the newer lol id is invalid.
1361   }
1362   if ((older_id != 0) && (older_lol == NULL)) {
1363     return Failure::Exception();  // Fail: the older lol id is invalid.
1364   }
1365 
1366   LolFilter filter(filter_obj);
1367   LolSummaryWriter writer(older_lol, newer_lol);
1368   return SummarizePrivate(&writer, &filter, false);
1369 }
1370 
1371 
1372 // Creates a summary report for the debugger.
1373 // Note: the SummaryWriter takes care of iterating over objects and filling in
1374 // the summary.
SummarizePrivate(SummaryWriter * writer,LolFilter * filter,bool is_tracking_roots)1375 MaybeObject* LiveObjectList::SummarizePrivate(SummaryWriter* writer,
1376                                               LolFilter* filter,
1377                                               bool is_tracking_roots) {
1378   HandleScope scope;
1379   MaybeObject* maybe_result;
1380 
1381   LiveObjectSummary summary(filter);
1382   writer->Write(&summary);
1383 
1384   // The result body will look like this:
1385   // body: {
1386   //   count: <total_count>,
1387   //   size: <total_size>,
1388   //   found_root: <boolean>,       // optional.
1389   //   found_weak_root: <boolean>,  // optional.
1390   //   summary: [
1391   //     {
1392   //       desc: "<object type name>",
1393   //       count: <count>,
1394   //       size: size
1395   //     },
1396   //     ...
1397   //   ]
1398   // }
1399 
1400   // Prefetch some needed symbols.
1401   Handle<String> desc_sym = Factory::LookupAsciiSymbol("desc");
1402   Handle<String> count_sym = Factory::LookupAsciiSymbol("count");
1403   Handle<String> size_sym = Factory::LookupAsciiSymbol("size");
1404   Handle<String> summary_sym = Factory::LookupAsciiSymbol("summary");
1405 
1406   // Allocate the summary array.
1407   int entries_count = summary.GetNumberOfEntries();
1408   Handle<FixedArray> summary_arr =
1409       Factory::NewFixedArray(entries_count);
1410   if (summary_arr->IsFailure()) return Object::cast(*summary_arr);
1411 
1412   int idx = 0;
1413   for (int i = 0; i < LiveObjectSummary::kNumberOfEntries; i++) {
1414     // Allocate the summary record.
1415     Handle<JSObject> detail = Factory::NewJSObject(Top::object_function());
1416     if (detail->IsFailure()) return Object::cast(*detail);
1417 
1418     // Fill in the summary record.
1419     LiveObjectType type = static_cast<LiveObjectType>(i);
1420     int count = summary.Count(type);
1421     if (count) {
1422       const char* desc_cstr = GetObjectTypeDesc(type);
1423       Handle<String> desc = Factory::LookupAsciiSymbol(desc_cstr);
1424       int size = summary.Size(type);
1425 
1426       maybe_result = detail->SetProperty(*desc_sym,
1427                                          *desc,
1428                                          NONE,
1429                                          kNonStrictMode);
1430       if (maybe_result->IsFailure()) return maybe_result;
1431       maybe_result = detail->SetProperty(*count_sym,
1432                                          Smi::FromInt(count),
1433                                          NONE,
1434                                          kNonStrictMode);
1435       if (maybe_result->IsFailure()) return maybe_result;
1436       maybe_result = detail->SetProperty(*size_sym,
1437                                          Smi::FromInt(size),
1438                                          NONE,
1439                                          kNonStrictMode);
1440       if (maybe_result->IsFailure()) return maybe_result;
1441 
1442       summary_arr->set(idx++, *detail);
1443     }
1444   }
1445 
1446   // Wrap the summary fixed array in a JS array.
1447   Handle<JSObject> summary_obj = Factory::NewJSObject(Top::array_function());
1448   if (summary_obj->IsFailure()) return Object::cast(*summary_obj);
1449   Handle<JSArray>::cast(summary_obj)->SetContent(*summary_arr);
1450 
1451   // Create the body object.
1452   Handle<JSObject> body = Factory::NewJSObject(Top::object_function());
1453   if (body->IsFailure()) return Object::cast(*body);
1454 
1455   // Fill out the body object.
1456   int total_count = summary.total_count();
1457   int total_size = summary.total_size();
1458   maybe_result = body->SetProperty(*count_sym,
1459                                    Smi::FromInt(total_count),
1460                                    NONE,
1461                                    kNonStrictMode);
1462   if (maybe_result->IsFailure()) return maybe_result;
1463 
1464   maybe_result = body->SetProperty(*size_sym,
1465                                    Smi::FromInt(total_size),
1466                                    NONE,
1467                                    kNonStrictMode);
1468   if (maybe_result->IsFailure()) return maybe_result;
1469 
1470   if (is_tracking_roots) {
1471     int found_root = summary.found_root();
1472     int found_weak_root = summary.found_weak_root();
1473     Handle<String> root_sym = Factory::LookupAsciiSymbol("found_root");
1474     Handle<String> weak_root_sym =
1475         Factory::LookupAsciiSymbol("found_weak_root");
1476     maybe_result = body->SetProperty(*root_sym,
1477                                      Smi::FromInt(found_root),
1478                                      NONE,
1479                                      kNonStrictMode);
1480     if (maybe_result->IsFailure()) return maybe_result;
1481     maybe_result = body->SetProperty(*weak_root_sym,
1482                                      Smi::FromInt(found_weak_root),
1483                                      NONE,
1484                                      kNonStrictMode);
1485     if (maybe_result->IsFailure()) return maybe_result;
1486   }
1487 
1488   maybe_result = body->SetProperty(*summary_sym,
1489                                    *summary_obj,
1490                                    NONE,
1491                                    kNonStrictMode);
1492   if (maybe_result->IsFailure()) return maybe_result;
1493 
1494   return *body;
1495 }
1496 
1497 
1498 // Returns an array listing the captured lols.
1499 // Note: only dumps the section starting at start_idx and only up to
1500 // dump_limit entries.
Info(int start_idx,int dump_limit)1501 MaybeObject* LiveObjectList::Info(int start_idx, int dump_limit) {
1502   HandleScope scope;
1503   MaybeObject* maybe_result;
1504 
1505   int total_count = LiveObjectList::list_count();
1506   int dump_count = total_count;
1507 
1508   // Adjust for where to start the dump.
1509   if (total_count == 0) {
1510       start_idx = 0;  // Ensure this to get an empty list.
1511   } else if ((start_idx < 0) || (start_idx >= total_count)) {
1512     return Failure::Exception();  // invalid start.
1513   }
1514   dump_count -= start_idx;
1515 
1516   // Adjust for the dump limit.
1517   if (dump_count > dump_limit) {
1518     dump_count = dump_limit;
1519   }
1520 
1521   // Allocate an array to hold the result.
1522   Handle<FixedArray> list = Factory::NewFixedArray(dump_count);
1523   if (list->IsFailure()) return Object::cast(*list);
1524 
1525   // Prefetch some needed symbols.
1526   Handle<String> id_sym = Factory::LookupAsciiSymbol("id");
1527   Handle<String> count_sym = Factory::LookupAsciiSymbol("count");
1528   Handle<String> size_sym = Factory::LookupAsciiSymbol("size");
1529 
1530   // Fill the array with the lol details.
1531   int idx = 0;
1532   LiveObjectList* lol = first_;
1533   while ((lol != NULL) && (idx < start_idx)) {  // Skip tail entries.
1534     if (lol->id() != 0) {
1535       idx++;
1536     }
1537     lol = lol->next();
1538   }
1539   idx = 0;
1540   while ((lol != NULL) && (dump_limit != 0)) {
1541     if (lol->id() != 0) {
1542       int count;
1543       int size;
1544       count = lol->GetTotalObjCountAndSize(&size);
1545 
1546       Handle<JSObject> detail = Factory::NewJSObject(Top::object_function());
1547       if (detail->IsFailure()) return Object::cast(*detail);
1548 
1549       maybe_result = detail->SetProperty(*id_sym,
1550                                          Smi::FromInt(lol->id()),
1551                                          NONE,
1552                                          kNonStrictMode);
1553       if (maybe_result->IsFailure()) return maybe_result;
1554       maybe_result = detail->SetProperty(*count_sym,
1555                                          Smi::FromInt(count),
1556                                          NONE,
1557                                          kNonStrictMode);
1558       if (maybe_result->IsFailure()) return maybe_result;
1559       maybe_result = detail->SetProperty(*size_sym,
1560                                          Smi::FromInt(size),
1561                                          NONE,
1562                                          kNonStrictMode);
1563       if (maybe_result->IsFailure()) return maybe_result;
1564       list->set(idx++, *detail);
1565       dump_limit--;
1566     }
1567     lol = lol->next();
1568   }
1569 
1570   // Return the result as a JS array.
1571   Handle<JSObject> lols = Factory::NewJSObject(Top::array_function());
1572   Handle<JSArray>::cast(lols)->SetContent(*list);
1573 
1574   Handle<JSObject> result = Factory::NewJSObject(Top::object_function());
1575   if (result->IsFailure()) return Object::cast(*result);
1576 
1577   maybe_result = result->SetProperty(*count_sym,
1578                                      Smi::FromInt(total_count),
1579                                      NONE,
1580                                      kNonStrictMode);
1581   if (maybe_result->IsFailure()) return maybe_result;
1582 
1583   Handle<String> first_sym = Factory::LookupAsciiSymbol("first_index");
1584   maybe_result = result->SetProperty(*first_sym,
1585                                      Smi::FromInt(start_idx),
1586                                      NONE,
1587                                      kNonStrictMode);
1588   if (maybe_result->IsFailure()) return maybe_result;
1589 
1590   Handle<String> lists_sym = Factory::LookupAsciiSymbol("lists");
1591   maybe_result = result->SetProperty(*lists_sym,
1592                                      *lols,
1593                                      NONE,
1594                                      kNonStrictMode);
1595   if (maybe_result->IsFailure()) return maybe_result;
1596 
1597   return *result;
1598 }
1599 
1600 
1601 // Deletes all captured lols.
Reset()1602 void LiveObjectList::Reset() {
1603   LiveObjectList *lol = last();
1604   // Just delete the last.  Each lol will delete it's prev automatically.
1605   delete lol;
1606 
1607   next_element_id_ = 1;
1608   list_count_ = 0;
1609   last_id_ = 0;
1610   first_ = NULL;
1611   last_ = NULL;
1612 }
1613 
1614 
1615 // Gets the object for the specified obj id.
GetObj(int obj_id)1616 Object* LiveObjectList::GetObj(int obj_id) {
1617   Element* element = FindElementFor<int>(GetElementId, obj_id);
1618   if (element != NULL) {
1619     return Object::cast(element->obj_);
1620   }
1621   return Heap::undefined_value();
1622 }
1623 
1624 
1625 // Gets the obj id for the specified address if valid.
GetObjId(Object * obj)1626 int LiveObjectList::GetObjId(Object* obj) {
1627   // Make a heap object pointer from the address.
1628   HeapObject* hobj = HeapObject::cast(obj);
1629   Element* element = FindElementFor<HeapObject*>(GetElementObj, hobj);
1630   if (element != NULL) {
1631     return element->id_;
1632   }
1633   return 0;  // Invalid address.
1634 }
1635 
1636 
1637 // Gets the obj id for the specified address if valid.
GetObjId(Handle<String> address)1638 Object* LiveObjectList::GetObjId(Handle<String> address) {
1639   SmartPointer<char> addr_str =
1640       address->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
1641 
1642   // Extract the address value from the string.
1643   int value = static_cast<int>(StringToInt(*address, 16));
1644   Object* obj = reinterpret_cast<Object*>(value);
1645   return Smi::FromInt(GetObjId(obj));
1646 }
1647 
1648 
1649 // Helper class for copying HeapObjects.
1650 class LolVisitor: public ObjectVisitor {
1651  public:
1652 
LolVisitor(HeapObject * target,Handle<HeapObject> handle_to_skip)1653   LolVisitor(HeapObject* target, Handle<HeapObject> handle_to_skip)
1654       : target_(target), handle_to_skip_(handle_to_skip), found_(false) {}
1655 
VisitPointer(Object ** p)1656   void VisitPointer(Object** p) { CheckPointer(p); }
1657 
VisitPointers(Object ** start,Object ** end)1658   void VisitPointers(Object** start, Object** end) {
1659     // Check all HeapObject pointers in [start, end).
1660     for (Object** p = start; !found() && p < end; p++) CheckPointer(p);
1661   }
1662 
found() const1663   inline bool found() const { return found_; }
reset()1664   inline bool reset() { return found_ = false; }
1665 
1666  private:
CheckPointer(Object ** p)1667   inline void CheckPointer(Object** p) {
1668     Object* object = *p;
1669     if (HeapObject::cast(object) == target_) {
1670       // We may want to skip this handle because the handle may be a local
1671       // handle in a handle scope in one of our callers.  Once we return,
1672       // that handle will be popped.  Hence, we don't want to count it as
1673       // a root that would have kept the target object alive.
1674       if (!handle_to_skip_.is_null() &&
1675           handle_to_skip_.location() == reinterpret_cast<HeapObject**>(p)) {
1676         return;  // Skip this handle.
1677       }
1678       found_ = true;
1679     }
1680   }
1681 
1682   HeapObject* target_;
1683   Handle<HeapObject> handle_to_skip_;
1684   bool found_;
1685 };
1686 
1687 
AddRootRetainerIfFound(const LolVisitor & visitor,LolFilter * filter,LiveObjectSummary * summary,void (* SetRootFound)(LiveObjectSummary * s),int start,int dump_limit,int * total_count,Handle<FixedArray> retainers_arr,int * count,int * index,const char * root_name,Handle<String> id_sym,Handle<String> desc_sym,Handle<String> size_sym,Handle<Object> error)1688 inline bool AddRootRetainerIfFound(const LolVisitor& visitor,
1689                                    LolFilter* filter,
1690                                    LiveObjectSummary *summary,
1691                                    void (*SetRootFound)(LiveObjectSummary *s),
1692                                    int start,
1693                                    int dump_limit,
1694                                    int* total_count,
1695                                    Handle<FixedArray> retainers_arr,
1696                                    int* count,
1697                                    int* index,
1698                                    const char* root_name,
1699                                    Handle<String> id_sym,
1700                                    Handle<String> desc_sym,
1701                                    Handle<String> size_sym,
1702                                    Handle<Object> error) {
1703   HandleScope scope;
1704 
1705   // Scratch handles.
1706   Handle<JSObject> detail;
1707   Handle<String> desc;
1708   Handle<HeapObject> retainer;
1709 
1710   if (visitor.found()) {
1711     if (!filter->is_active()) {
1712       (*total_count)++;
1713       if (summary) {
1714         SetRootFound(summary);
1715       } else if ((*total_count > start) && ((*index) < dump_limit)) {
1716         (*count)++;
1717         if (!retainers_arr.is_null()) {
1718           return AddObjDetail(retainers_arr,
1719                               (*index)++,
1720                               0,
1721                               retainer,
1722                               root_name,
1723                               id_sym,
1724                               desc_sym,
1725                               size_sym,
1726                               detail,
1727                               desc,
1728                               error);
1729         }
1730       }
1731     }
1732   }
1733   return true;
1734 }
1735 
1736 
SetFoundRoot(LiveObjectSummary * summary)1737 inline void SetFoundRoot(LiveObjectSummary *summary) {
1738   summary->set_found_root();
1739 }
1740 
1741 
SetFoundWeakRoot(LiveObjectSummary * summary)1742 inline void SetFoundWeakRoot(LiveObjectSummary *summary) {
1743   summary->set_found_weak_root();
1744 }
1745 
1746 
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)1747 int LiveObjectList::GetRetainers(Handle<HeapObject> target,
1748                                  Handle<JSObject> instance_filter,
1749                                  Handle<FixedArray> retainers_arr,
1750                                  int start,
1751                                  int dump_limit,
1752                                  int* total_count,
1753                                  LolFilter* filter,
1754                                  LiveObjectSummary *summary,
1755                                  JSFunction* arguments_function,
1756                                  Handle<Object> error) {
1757   HandleScope scope;
1758 
1759   // Scratch handles.
1760   Handle<JSObject> detail;
1761   Handle<String> desc;
1762   Handle<HeapObject> retainer;
1763 
1764   // Prefetch some needed symbols.
1765   Handle<String> id_sym = Factory::LookupAsciiSymbol("id");
1766   Handle<String> desc_sym = Factory::LookupAsciiSymbol("desc");
1767   Handle<String> size_sym = Factory::LookupAsciiSymbol("size");
1768 
1769   NoHandleAllocation ha;
1770   int count = 0;
1771   int index = 0;
1772   Handle<JSObject> last_obj;
1773 
1774   *total_count = 0;
1775 
1776   // Iterate roots.
1777   LolVisitor lol_visitor(*target, target);
1778   Heap::IterateStrongRoots(&lol_visitor, VISIT_ALL);
1779   if (!AddRootRetainerIfFound(lol_visitor,
1780                               filter,
1781                               summary,
1782                               SetFoundRoot,
1783                               start,
1784                               dump_limit,
1785                               total_count,
1786                               retainers_arr,
1787                               &count,
1788                               &index,
1789                               "<root>",
1790                               id_sym,
1791                               desc_sym,
1792                               size_sym,
1793                               error)) {
1794     return -1;
1795   }
1796 
1797   lol_visitor.reset();
1798   Heap::IterateWeakRoots(&lol_visitor, VISIT_ALL);
1799   if (!AddRootRetainerIfFound(lol_visitor,
1800                               filter,
1801                               summary,
1802                               SetFoundWeakRoot,
1803                               start,
1804                               dump_limit,
1805                               total_count,
1806                               retainers_arr,
1807                               &count,
1808                               &index,
1809                               "<weak root>",
1810                               id_sym,
1811                               desc_sym,
1812                               size_sym,
1813                               error)) {
1814     return -1;
1815   }
1816 
1817   // Iterate the live object lists.
1818   LolIterator it(NULL, last());
1819   for (it.Init(); !it.Done() && (index < dump_limit); it.Next()) {
1820     HeapObject* heap_obj = it.Obj();
1821 
1822     // Only look at all JSObjects.
1823     if (heap_obj->IsJSObject()) {
1824       // Skip context extension objects and argument arrays as these are
1825       // checked in the context of functions using them.
1826       JSObject* obj = JSObject::cast(heap_obj);
1827       if (obj->IsJSContextExtensionObject() ||
1828           obj->map()->constructor() == arguments_function) {
1829         continue;
1830       }
1831 
1832       // Check if the JS object has a reference to the object looked for.
1833       if (obj->ReferencesObject(*target)) {
1834         // Check instance filter if supplied. This is normally used to avoid
1835         // references from mirror objects (see Runtime_IsInPrototypeChain).
1836         if (!instance_filter->IsUndefined()) {
1837           Object* V = obj;
1838           while (true) {
1839             Object* prototype = V->GetPrototype();
1840             if (prototype->IsNull()) {
1841               break;
1842             }
1843             if (*instance_filter == prototype) {
1844               obj = NULL;  // Don't add this object.
1845               break;
1846             }
1847             V = prototype;
1848           }
1849         }
1850 
1851         if (obj != NULL) {
1852           // Skip objects that have been filtered out.
1853           if (filter->Matches(heap_obj)) {
1854             continue;
1855           }
1856 
1857           // Valid reference found add to instance array if supplied an update
1858           // count.
1859           last_obj = Handle<JSObject>(obj);
1860           (*total_count)++;
1861 
1862           if (summary != NULL) {
1863             summary->Add(heap_obj);
1864           } else if ((*total_count > start) && (index < dump_limit)) {
1865             count++;
1866             if (!retainers_arr.is_null()) {
1867               retainer = Handle<HeapObject>(heap_obj);
1868               bool success = AddObjDetail(retainers_arr,
1869                                           index++,
1870                                           it.Id(),
1871                                           retainer,
1872                                           NULL,
1873                                           id_sym,
1874                                           desc_sym,
1875                                           size_sym,
1876                                           detail,
1877                                           desc,
1878                                           error);
1879               if (!success) return -1;
1880             }
1881           }
1882         }
1883       }
1884     }
1885   }
1886 
1887   // Check for circular reference only. This can happen when the object is only
1888   // referenced from mirrors and has a circular reference in which case the
1889   // object is not really alive and would have been garbage collected if not
1890   // referenced from the mirror.
1891 
1892   if (*total_count == 1 && !last_obj.is_null() && *last_obj == *target) {
1893     count = 0;
1894     *total_count = 0;
1895   }
1896 
1897   return count;
1898 }
1899 
1900 
GetObjRetainers(int obj_id,Handle<JSObject> instance_filter,bool verbose,int start,int dump_limit,Handle<JSObject> filter_obj)1901 MaybeObject* LiveObjectList::GetObjRetainers(int obj_id,
1902                                              Handle<JSObject> instance_filter,
1903                                              bool verbose,
1904                                              int start,
1905                                              int dump_limit,
1906                                              Handle<JSObject> filter_obj) {
1907   HandleScope scope;
1908 
1909   // Get the target object.
1910   HeapObject* heap_obj = HeapObject::cast(GetObj(obj_id));
1911   if (heap_obj == Heap::undefined_value()) {
1912     return heap_obj;
1913   }
1914 
1915   Handle<HeapObject> target = Handle<HeapObject>(heap_obj);
1916 
1917   // Get the constructor function for context extension and arguments array.
1918   JSObject* arguments_boilerplate =
1919       Top::context()->global_context()->arguments_boilerplate();
1920   JSFunction* arguments_function =
1921       JSFunction::cast(arguments_boilerplate->map()->constructor());
1922 
1923   Handle<JSFunction> args_function = Handle<JSFunction>(arguments_function);
1924   LolFilter filter(filter_obj);
1925 
1926   if (!verbose) {
1927     RetainersSummaryWriter writer(target, instance_filter, args_function);
1928     return SummarizePrivate(&writer, &filter, true);
1929 
1930   } else {
1931     RetainersDumpWriter writer(target, instance_filter, args_function);
1932     Object* body_obj;
1933     MaybeObject* maybe_result =
1934         DumpPrivate(&writer, start, dump_limit, &filter);
1935     if (!maybe_result->ToObject(&body_obj)) {
1936       return maybe_result;
1937     }
1938 
1939     // Set body.id.
1940     Handle<JSObject> body = Handle<JSObject>(JSObject::cast(body_obj));
1941     Handle<String> id_sym = Factory::LookupAsciiSymbol("id");
1942     maybe_result = body->SetProperty(*id_sym,
1943                                      Smi::FromInt(obj_id),
1944                                      NONE,
1945                                      kNonStrictMode);
1946     if (maybe_result->IsFailure()) return maybe_result;
1947 
1948     return *body;
1949   }
1950 }
1951 
1952 
PrintObj(int obj_id)1953 Object* LiveObjectList::PrintObj(int obj_id) {
1954   Object* obj = GetObj(obj_id);
1955   if (!obj) {
1956     return Heap::undefined_value();
1957   }
1958 
1959   EmbeddedVector<char, 128> temp_filename;
1960   static int temp_count = 0;
1961   const char* path_prefix = ".";
1962 
1963   if (FLAG_lol_workdir) {
1964     path_prefix = FLAG_lol_workdir;
1965   }
1966   OS::SNPrintF(temp_filename, "%s/lol-print-%d", path_prefix, ++temp_count);
1967 
1968   FILE* f = OS::FOpen(temp_filename.start(), "w+");
1969 
1970   PrintF(f, "@%d ", LiveObjectList::GetObjId(obj));
1971 #ifdef OBJECT_PRINT
1972 #ifdef INSPECTOR
1973   Inspector::DumpObjectType(f, obj);
1974 #endif  // INSPECTOR
1975   PrintF(f, "\n");
1976   obj->Print(f);
1977 #else  // !OBJECT_PRINT
1978   obj->ShortPrint(f);
1979 #endif  // !OBJECT_PRINT
1980   PrintF(f, "\n");
1981   Flush(f);
1982   fclose(f);
1983 
1984   // Create a string from the temp_file.
1985   // Note: the mmapped resource will take care of closing the file.
1986   MemoryMappedExternalResource* resource =
1987       new MemoryMappedExternalResource(temp_filename.start(), true);
1988   if (resource->exists() && !resource->is_empty()) {
1989     ASSERT(resource->IsAscii());
1990     Handle<String> dump_string =
1991         Factory::NewExternalStringFromAscii(resource);
1992     ExternalStringTable::AddString(*dump_string);
1993     return *dump_string;
1994   } else {
1995     delete resource;
1996   }
1997   return Heap::undefined_value();
1998 }
1999 
2000 
2001 class LolPathTracer: public PathTracer {
2002  public:
LolPathTracer(FILE * out,Object * search_target,WhatToFind what_to_find)2003   LolPathTracer(FILE* out,
2004                 Object* search_target,
2005                 WhatToFind what_to_find)
2006       : PathTracer(search_target, what_to_find, VISIT_ONLY_STRONG), out_(out) {}
2007 
2008  private:
2009   void ProcessResults();
2010 
2011   FILE* out_;
2012 };
2013 
2014 
ProcessResults()2015 void LolPathTracer::ProcessResults() {
2016   if (found_target_) {
2017     PrintF(out_, "=====================================\n");
2018     PrintF(out_, "====        Path to object       ====\n");
2019     PrintF(out_, "=====================================\n\n");
2020 
2021     ASSERT(!object_stack_.is_empty());
2022     Object* prev = NULL;
2023     for (int i = 0, index = 0; i < object_stack_.length(); i++) {
2024       Object* obj = object_stack_[i];
2025 
2026       // Skip this object if it is basically the internals of the
2027       // previous object (which would have dumped its details already).
2028       if (prev && prev->IsJSObject() &&
2029           (obj != search_target_)) {
2030         JSObject* jsobj = JSObject::cast(prev);
2031         if (obj->IsFixedArray() &&
2032             jsobj->properties() == FixedArray::cast(obj)) {
2033           // Skip this one because it would have been printed as the
2034           // properties of the last object already.
2035           continue;
2036         } else if (obj->IsHeapObject() &&
2037                    jsobj->elements() == HeapObject::cast(obj)) {
2038           // Skip this one because it would have been printed as the
2039           // elements of the last object already.
2040           continue;
2041         }
2042       }
2043 
2044       // Print a connecting arrow.
2045       if (i > 0) PrintF(out_, "\n     |\n     |\n     V\n\n");
2046 
2047       // Print the object index.
2048       PrintF(out_, "[%d] ", ++index);
2049 
2050       // Print the LOL object ID:
2051       int id = LiveObjectList::GetObjId(obj);
2052       if (id > 0) PrintF(out_, "@%d ", id);
2053 
2054 #ifdef OBJECT_PRINT
2055 #ifdef INSPECTOR
2056       Inspector::DumpObjectType(out_, obj);
2057 #endif  // INSPECTOR
2058       PrintF(out_, "\n");
2059       obj->Print(out_);
2060 #else  // !OBJECT_PRINT
2061       obj->ShortPrint(out_);
2062       PrintF(out_, "\n");
2063 #endif  // !OBJECT_PRINT
2064       Flush(out_);
2065     }
2066     PrintF(out_, "\n");
2067     PrintF(out_, "=====================================\n\n");
2068     Flush(out_);
2069   }
2070 }
2071 
2072 
GetPathPrivate(HeapObject * obj1,HeapObject * obj2)2073 Object* LiveObjectList::GetPathPrivate(HeapObject* obj1, HeapObject* obj2) {
2074   EmbeddedVector<char, 128> temp_filename;
2075   static int temp_count = 0;
2076   const char* path_prefix = ".";
2077 
2078   if (FLAG_lol_workdir) {
2079     path_prefix = FLAG_lol_workdir;
2080   }
2081   OS::SNPrintF(temp_filename, "%s/lol-getpath-%d", path_prefix, ++temp_count);
2082 
2083   FILE* f = OS::FOpen(temp_filename.start(), "w+");
2084 
2085   // Save the previous verbosity.
2086   bool prev_verbosity = FLAG_use_verbose_printer;
2087   FLAG_use_verbose_printer = false;
2088 
2089   // Dump the paths.
2090   {
2091     // The tracer needs to be scoped because its usage asserts no allocation,
2092     // and we need to allocate the result string below.
2093     LolPathTracer tracer(f, obj2, LolPathTracer::FIND_FIRST);
2094 
2095     bool found = false;
2096     if (obj1 == NULL) {
2097       // Check for ObjectGroups that references this object.
2098       // TODO(mlam): refactor this to be more modular.
2099       {
2100         List<ObjectGroup*>* groups = GlobalHandles::ObjectGroups();
2101         for (int i = 0; i < groups->length(); i++) {
2102           ObjectGroup* group = groups->at(i);
2103           if (group == NULL) continue;
2104 
2105           bool found_group = false;
2106           List<Object**>& objects = group->objects_;
2107           for (int j = 0; j < objects.length(); j++) {
2108             Object* object = *objects[j];
2109             HeapObject* hobj = HeapObject::cast(object);
2110             if (obj2 == hobj) {
2111               found_group = true;
2112               break;
2113             }
2114           }
2115 
2116           if (found_group) {
2117             PrintF(f,
2118                    "obj %p is a member of object group %p {\n",
2119                    reinterpret_cast<void*>(obj2),
2120                    reinterpret_cast<void*>(group));
2121             for (int j = 0; j < objects.length(); j++) {
2122               Object* object = *objects[j];
2123               if (!object->IsHeapObject()) continue;
2124 
2125               HeapObject* hobj = HeapObject::cast(object);
2126               int id = GetObjId(hobj);
2127               if (id != 0) {
2128                 PrintF(f, "  @%d:", id);
2129               } else {
2130                 PrintF(f, "  <no id>:");
2131               }
2132 
2133               char buffer[512];
2134               GenerateObjectDesc(hobj, buffer, sizeof(buffer));
2135               PrintF(f, " %s", buffer);
2136               if (hobj == obj2) {
2137                 PrintF(f, " <===");
2138               }
2139               PrintF(f, "\n");
2140             }
2141             PrintF(f, "}\n");
2142           }
2143         }
2144       }
2145 
2146       PrintF(f, "path from roots to obj %p\n", reinterpret_cast<void*>(obj2));
2147       Heap::IterateRoots(&tracer, VISIT_ONLY_STRONG);
2148       found = tracer.found();
2149 
2150       if (!found) {
2151         PrintF(f, "  No paths found. Checking symbol tables ...\n");
2152         SymbolTable* symbol_table = Heap::raw_unchecked_symbol_table();
2153         tracer.VisitPointers(reinterpret_cast<Object**>(&symbol_table),
2154                              reinterpret_cast<Object**>(&symbol_table)+1);
2155         found = tracer.found();
2156         if (!found) {
2157           symbol_table->IteratePrefix(&tracer);
2158           found = tracer.found();
2159         }
2160       }
2161 
2162       if (!found) {
2163         PrintF(f, "  No paths found. Checking weak roots ...\n");
2164         // Check weak refs next.
2165         GlobalHandles::IterateWeakRoots(&tracer);
2166         found = tracer.found();
2167       }
2168 
2169     } else {
2170       PrintF(f, "path from obj %p to obj %p:\n",
2171              reinterpret_cast<void*>(obj1), reinterpret_cast<void*>(obj2));
2172       tracer.TracePathFrom(reinterpret_cast<Object**>(&obj1));
2173       found = tracer.found();
2174     }
2175 
2176     if (!found) {
2177       PrintF(f, "  No paths found\n\n");
2178     }
2179   }
2180 
2181   // Flush and clean up the dumped file.
2182   Flush(f);
2183   fclose(f);
2184 
2185   // Restore the previous verbosity.
2186   FLAG_use_verbose_printer = prev_verbosity;
2187 
2188   // Create a string from the temp_file.
2189   // Note: the mmapped resource will take care of closing the file.
2190   MemoryMappedExternalResource* resource =
2191       new MemoryMappedExternalResource(temp_filename.start(), true);
2192   if (resource->exists() && !resource->is_empty()) {
2193     ASSERT(resource->IsAscii());
2194     Handle<String> path_string =
2195         Factory::NewExternalStringFromAscii(resource);
2196     ExternalStringTable::AddString(*path_string);
2197     return *path_string;
2198   } else {
2199     delete resource;
2200   }
2201   return Heap::undefined_value();
2202 }
2203 
2204 
GetPath(int obj_id1,int obj_id2,Handle<JSObject> instance_filter)2205 Object* LiveObjectList::GetPath(int obj_id1,
2206                                 int obj_id2,
2207                                 Handle<JSObject> instance_filter) {
2208   HandleScope scope;
2209 
2210   // Get the target object.
2211   HeapObject* obj1 = NULL;
2212   if (obj_id1 != 0) {
2213     obj1 = HeapObject::cast(GetObj(obj_id1));
2214     if (obj1 == Heap::undefined_value()) {
2215       return obj1;
2216     }
2217   }
2218 
2219   HeapObject* obj2 = HeapObject::cast(GetObj(obj_id2));
2220   if (obj2 == Heap::undefined_value()) {
2221     return obj2;
2222   }
2223 
2224   return GetPathPrivate(obj1, obj2);
2225 }
2226 
2227 
DoProcessNonLive(HeapObject * obj)2228 void LiveObjectList::DoProcessNonLive(HeapObject *obj) {
2229   // We should only be called if we have at least one lol to search.
2230   ASSERT(last() != NULL);
2231   Element* element = last()->Find(obj);
2232   if (element != NULL) {
2233     NullifyNonLivePointer(&element->obj_);
2234   }
2235 }
2236 
2237 
IterateElementsPrivate(ObjectVisitor * v)2238 void LiveObjectList::IterateElementsPrivate(ObjectVisitor* v) {
2239   LiveObjectList* lol = last();
2240   while (lol != NULL) {
2241     Element* elements = lol->elements_;
2242     int count = lol->obj_count_;
2243     for (int i = 0; i < count; i++) {
2244       HeapObject** p = &elements[i].obj_;
2245       v->VisitPointer(reinterpret_cast<Object **>(p));
2246     }
2247     lol = lol->prev_;
2248   }
2249 }
2250 
2251 
2252 // Purpose: Called by GCEpilogue to purge duplicates.  Not to be called by
2253 // anyone else.
PurgeDuplicates()2254 void LiveObjectList::PurgeDuplicates() {
2255   bool is_sorted = false;
2256   LiveObjectList* lol = last();
2257   if (!lol) {
2258     return;  // Nothing to purge.
2259   }
2260 
2261   int total_count = lol->TotalObjCount();
2262   if (!total_count) {
2263     return;  // Nothing to purge.
2264   }
2265 
2266   Element* elements = NewArray<Element>(total_count);
2267   int count = 0;
2268 
2269   // Copy all the object elements into a consecutive array.
2270   while (lol) {
2271     memcpy(&elements[count], lol->elements_, lol->obj_count_ * sizeof(Element));
2272     count += lol->obj_count_;
2273     lol = lol->prev_;
2274   }
2275   qsort(elements, total_count, sizeof(Element),
2276         reinterpret_cast<RawComparer>(CompareElement));
2277 
2278   ASSERT(count == total_count);
2279 
2280   // Iterate over all objects in the consolidated list and check for dups.
2281   total_count--;
2282   for (int i = 0; i < total_count; ) {
2283     Element* curr = &elements[i];
2284     HeapObject* curr_obj = curr->obj_;
2285     int j = i+1;
2286     bool done = false;
2287 
2288     while (!done && (j < total_count)) {
2289       // Process if the element's object is still live after the current GC.
2290       // Non-live objects will be converted to SMIs i.e. not HeapObjects.
2291       if (curr_obj->IsHeapObject()) {
2292         Element* next = &elements[j];
2293         HeapObject* next_obj = next->obj_;
2294         if (next_obj->IsHeapObject()) {
2295           if (curr_obj != next_obj) {
2296             done = true;
2297             continue;  // Live object but no match.  Move on.
2298           }
2299 
2300           // NOTE: we've just GCed the LOLs.  Hence, they are no longer sorted.
2301           // Since we detected at least one need to search for entries, we'll
2302           // sort it to enable the use of NullifyMostRecent() below.  We only
2303           // need to sort it once (except for one exception ... see below).
2304           if (!is_sorted) {
2305             SortAll();
2306             is_sorted = true;
2307           }
2308 
2309           // We have a match.  Need to nullify the most recent ref to this
2310           // object.  We'll keep the oldest ref:
2311           // Note: we will nullify the element record in the LOL
2312           // database, not in the local sorted copy of the elements.
2313           NullifyMostRecent(curr_obj);
2314         }
2315       }
2316       // Either the object was already marked for purging, or we just marked
2317       // it.  Either way, if there's more than one dup, then we need to check
2318       // the next element for another possible dup against the current as well
2319       // before we move on.  So, here we go.
2320       j++;
2321     }
2322 
2323     // We can move on to checking the match on the next element.
2324     i = j;
2325   }
2326 
2327   DeleteArray<Element>(elements);
2328 }
2329 
2330 
2331 // Purpose: Purges dead objects and resorts the LOLs.
GCEpiloguePrivate()2332 void LiveObjectList::GCEpiloguePrivate() {
2333   // Note: During the GC, ConsStrings may be collected and pointers may be
2334   // forwarded to its constituent string.  As a result, we may find dupes of
2335   // objects references in the LOL list.
2336   // Another common way we get dups is that free chunks that have been swept
2337   // in the oldGen heap may be kept as ByteArray objects in a free list.
2338   //
2339   // When we promote live objects from the youngGen, the object may be moved
2340   // to the start of these free chunks.  Since there is no free or move event
2341   // for the free chunks, their addresses will show up 2 times: once for their
2342   // original free ByteArray selves, and once for the newly promoted youngGen
2343   // object.  Hence, we can get a duplicate address in the LOL again.
2344   //
2345   // We need to eliminate these dups because the LOL implementation expects to
2346   // only have at most one unique LOL reference to any object at any time.
2347   PurgeDuplicates();
2348 
2349   // After the GC, sweep away all free'd Elements and compact.
2350   LiveObjectList *prev = NULL;
2351   LiveObjectList *next = NULL;
2352 
2353   // Iterating from the youngest lol to the oldest lol.
2354   for (LiveObjectList *lol = last(); lol; lol = prev) {
2355     Element* elements = lol->elements_;
2356     prev = lol->prev();  // Save the prev.
2357 
2358     // Remove any references to collected objects.
2359     int i = 0;
2360     while (i < lol->obj_count_) {
2361       Element& element = elements[i];
2362       if (!element.obj_->IsHeapObject()) {
2363         // If the HeapObject address was converted into a SMI, then this
2364         // is a dead object.  Copy the last element over this one.
2365         element = elements[lol->obj_count_ - 1];
2366         lol->obj_count_--;
2367         // We've just moved the last element into this index.  We'll revisit
2368         // this index again.  Hence, no need to increment the iterator.
2369       } else {
2370         i++;  // Look at the next element next.
2371       }
2372     }
2373 
2374     int new_count = lol->obj_count_;
2375 
2376     // Check if there are any more elements to keep after purging the dead ones.
2377     if (new_count == 0) {
2378       DeleteArray<Element>(elements);
2379       lol->elements_ = NULL;
2380       lol->capacity_ = 0;
2381       ASSERT(lol->obj_count_ == 0);
2382 
2383       // If the list is also invisible, the clean up the list as well.
2384       if (lol->id_ == 0) {
2385         // Point the next lol's prev to this lol's prev.
2386         if (next) {
2387           next->prev_ = lol->prev_;
2388         } else {
2389           last_ = lol->prev_;
2390         }
2391 
2392         // Delete this now empty and invisible lol.
2393         delete lol;
2394 
2395         // Don't point the next to this lol since it is now deleted.
2396         // Leave the next pointer pointing to the current lol.
2397         continue;
2398       }
2399 
2400     } else {
2401       // If the obj_count_ is less than the capacity and the difference is
2402       // greater than a specified threshold, then we should shrink the list.
2403       int diff = lol->capacity_ - new_count;
2404       const int kMaxUnusedSpace = 64;
2405       if (diff > kMaxUnusedSpace) {  // Threshold for shrinking.
2406         // Shrink the list.
2407         Element *new_elements = NewArray<Element>(new_count);
2408         memcpy(new_elements, elements, new_count * sizeof(Element));
2409 
2410         DeleteArray<Element>(elements);
2411         lol->elements_ = new_elements;
2412         lol->capacity_ = new_count;
2413       }
2414       ASSERT(lol->obj_count_ == new_count);
2415 
2416       lol->Sort();  // We've moved objects.  Re-sort in case.
2417     }
2418 
2419     // Save the next (for the previous link) in case we need it later.
2420     next = lol;
2421   }
2422 
2423 #ifdef VERIFY_LOL
2424   if (FLAG_verify_lol) {
2425     Verify();
2426   }
2427 #endif
2428 }
2429 
2430 
2431 #ifdef VERIFY_LOL
Verify(bool match_heap_exactly)2432 void LiveObjectList::Verify(bool match_heap_exactly) {
2433   OS::Print("Verifying the LiveObjectList database:\n");
2434 
2435   LiveObjectList* lol = last();
2436   if (lol == NULL) {
2437     OS::Print("  No lol database to verify\n");
2438     return;
2439   }
2440 
2441   OS::Print("  Preparing the lol database ...\n");
2442   int total_count = lol->TotalObjCount();
2443 
2444   Element* elements = NewArray<Element>(total_count);
2445   int count = 0;
2446 
2447   // Copy all the object elements into a consecutive array.
2448   OS::Print("  Copying the lol database ...\n");
2449   while (lol != NULL) {
2450     memcpy(&elements[count], lol->elements_, lol->obj_count_ * sizeof(Element));
2451     count += lol->obj_count_;
2452     lol = lol->prev_;
2453   }
2454   qsort(elements, total_count, sizeof(Element),
2455         reinterpret_cast<RawComparer>(CompareElement));
2456 
2457   ASSERT(count == total_count);
2458 
2459   // Iterate over all objects in the heap and check for:
2460   // 1. object in LOL but not in heap i.e. error.
2461   // 2. object in heap but not in LOL (possibly not an error).  Usually
2462   //    just means that we don't have the a capture of the latest heap.
2463   //    That is unless we did this verify immediately after a capture,
2464   //    and specified match_heap_exactly = true.
2465 
2466   int number_of_heap_objects = 0;
2467   int number_of_matches = 0;
2468   int number_not_in_heap = total_count;
2469   int number_not_in_lol = 0;
2470 
2471   OS::Print("  Start verify ...\n");
2472   OS::Print("  Verifying ...");
2473   Flush();
2474   HeapIterator iterator(HeapIterator::kFilterFreeListNodes);
2475   HeapObject* heap_obj = NULL;
2476   while ((heap_obj = iterator.next()) != NULL) {
2477     number_of_heap_objects++;
2478 
2479     // Check if the heap_obj is in the lol.
2480     Element key;
2481     key.obj_ = heap_obj;
2482 
2483     Element* result = reinterpret_cast<Element*>(
2484         bsearch(&key, elements, total_count, sizeof(Element),
2485                 reinterpret_cast<RawComparer>(CompareElement)));
2486 
2487     if (result != NULL) {
2488       number_of_matches++;
2489       number_not_in_heap--;
2490       // Mark it as found by changing it into a SMI (mask off low bit).
2491       // Note: we cannot use HeapObject::cast() here because it asserts that
2492       // the HeapObject bit is set on the address, but we're unsetting it on
2493       // purpose here for our marking.
2494       result->obj_ = reinterpret_cast<HeapObject*>(heap_obj->address());
2495 
2496     } else {
2497       number_not_in_lol++;
2498       if (match_heap_exactly) {
2499         OS::Print("heap object %p NOT in lol database\n", heap_obj);
2500       }
2501     }
2502     // Show some sign of life.
2503     if (number_of_heap_objects % 1000 == 0) {
2504       OS::Print(".");
2505       fflush(stdout);
2506     }
2507   }
2508   OS::Print("\n");
2509 
2510   // Reporting lol objects not found in the heap.
2511   if (number_not_in_heap) {
2512     int found = 0;
2513     for (int i = 0; (i < total_count) && (found < number_not_in_heap); i++) {
2514       Element& element = elements[i];
2515       if (element.obj_->IsHeapObject()) {
2516         OS::Print("lol database object [%d of %d] %p NOT in heap\n",
2517                   i, total_count, element.obj_);
2518         found++;
2519       }
2520     }
2521   }
2522 
2523   DeleteArray<Element>(elements);
2524 
2525   OS::Print("number of objects in lol database %d\n", total_count);
2526   OS::Print("number of heap objects .......... %d\n", number_of_heap_objects);
2527   OS::Print("number of matches ............... %d\n", number_of_matches);
2528   OS::Print("number NOT in heap .............. %d\n", number_not_in_heap);
2529   OS::Print("number NOT in lol database ...... %d\n", number_not_in_lol);
2530 
2531   if (number_of_matches != total_count) {
2532     OS::Print("  *** ERROR: "
2533               "NOT all lol database objects match heap objects.\n");
2534   }
2535   if (number_not_in_heap != 0) {
2536     OS::Print("  *** ERROR: %d lol database objects not found in heap.\n",
2537               number_not_in_heap);
2538   }
2539   if (match_heap_exactly) {
2540     if (!(number_not_in_lol == 0)) {
2541       OS::Print("  *** ERROR: %d heap objects NOT found in lol database.\n",
2542                 number_not_in_lol);
2543     }
2544   }
2545 
2546   ASSERT(number_of_matches == total_count);
2547   ASSERT(number_not_in_heap == 0);
2548   ASSERT(number_not_in_lol == (number_of_heap_objects - total_count));
2549   if (match_heap_exactly) {
2550     ASSERT(total_count == number_of_heap_objects);
2551     ASSERT(number_not_in_lol == 0);
2552   }
2553 
2554   OS::Print("  Verify the lol database is sorted ...\n");
2555   lol = last();
2556   while (lol != NULL) {
2557     Element* elements = lol->elements_;
2558     for (int i = 0; i < lol->obj_count_ - 1; i++) {
2559       if (elements[i].obj_ >= elements[i+1].obj_) {
2560         OS::Print("  *** ERROR: lol %p obj[%d] %p > obj[%d] %p\n",
2561                   lol, i, elements[i].obj_, i+1, elements[i+1].obj_);
2562       }
2563     }
2564     lol = lol->prev_;
2565   }
2566 
2567   OS::Print("  DONE verifying.\n\n\n");
2568 }
2569 
2570 
VerifyNotInFromSpace()2571 void LiveObjectList::VerifyNotInFromSpace() {
2572   OS::Print("VerifyNotInFromSpace() ...\n");
2573   LolIterator it(NULL, last());
2574   int i = 0;
2575   for (it.Init(); !it.Done(); it.Next()) {
2576     HeapObject* heap_obj = it.Obj();
2577     if (Heap::InFromSpace(heap_obj)) {
2578       OS::Print(" ERROR: VerifyNotInFromSpace: [%d] obj %p in From space %p\n",
2579                 i++, heap_obj, Heap::new_space()->FromSpaceLow());
2580     }
2581   }
2582 }
2583 #endif  // VERIFY_LOL
2584 
2585 
2586 } }  // namespace v8::internal
2587 
2588 #endif  // LIVE_OBJECT_LIST
2589 
2590