Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(73)

Side by Side Diff: src/liveobjectlist.cc

Issue 6357005: Adding files for LiveObjectList implementation. (Closed) Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/
Patch Set: '' Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/liveobjectlist.h ('k') | src/liveobjectlist-inl.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2011 the V8 project authors. All rights reserved. 1 // Copyright 2011 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without 2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are 3 // modification, are permitted provided that the following conditions are
4 // met: 4 // met:
5 // 5 //
6 // * Redistributions of source code must retain the above copyright 6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer. 7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above 8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following 9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided 10 // disclaimer in the documentation and/or other materials provided
(...skipping 19 matching lines...) Expand all
30 #include <ctype.h> 30 #include <ctype.h>
31 #include <stdlib.h> 31 #include <stdlib.h>
32 32
33 #include "v8.h" 33 #include "v8.h"
34 34
35 #include "checks.h" 35 #include "checks.h"
36 #include "global-handles.h" 36 #include "global-handles.h"
37 #include "heap.h" 37 #include "heap.h"
38 #include "inspector.h" 38 #include "inspector.h"
39 #include "list-inl.h" 39 #include "list-inl.h"
40 #include "liveobjectlist.h" 40 #include "liveobjectlist-inl.h"
41 #include "string-stream.h" 41 #include "string-stream.h"
42 #include "top.h" 42 #include "top.h"
43 #include "v8utils.h" 43 #include "v8utils.h"
44 44
45 namespace v8 { 45 namespace v8 {
46 namespace internal { 46 namespace internal {
47 47
48 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
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
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
161 const char* GetObjectTypeDesc(HeapObject* heap_obj) {
162 LiveObjectType type = GetObjectType(heap_obj);
163 return GetObjectTypeDesc(type);
164 }
165
166
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
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
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
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
256 inline bool is_active() const { return is_active_; }
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
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
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
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
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
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:
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
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
369 inline bool Done() const {
370 return (curr_ == older_);
371 }
372
373 // Object level iteration.
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
388 inline int Id() const {
389 return elements_[index_].id_;
390 }
391 inline HeapObject* Obj() const {
392 return elements_[index_].obj_;
393 }
394
395 inline int LolObjCount() const {
396 if (curr_ != NULL) return curr_->obj_count_;
397 return 0;
398 }
399
400 protected:
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:
421 LolForwardIterator(LiveObjectList* first, LiveObjectList* last)
422 : LolIterator(first, last) {
423 }
424
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
434 inline bool Done() const {
435 return (curr_ == newer_);
436 }
437
438 // Object level iteration.
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.
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.
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.
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 =
596 detail->SetProperty(*id_sym, Smi::FromInt(obj_id), NONE);
597 if (maybe_result->IsFailure()) return false;
598 }
599 { MaybeObject* maybe_result =
600 detail->SetProperty(*desc_sym, *desc, NONE);
601 if (maybe_result->IsFailure()) return false;
602 }
603 { MaybeObject* maybe_result =
604 detail->SetProperty(*size_sym, Smi::FromInt(size), NONE);
605 if (maybe_result->IsFailure()) return false;
606 }
607
608 arr->set(index, *detail);
609 return true;
610 }
611
612
613 class DumpWriter {
614 public:
615 virtual ~DumpWriter() {}
616
617 virtual void ComputeTotalCountAndSize(LolFilter* filter,
618 int* count,
619 int* size) = 0;
620 virtual bool Write(Handle<FixedArray> elements_arr,
621 int start,
622 int dump_limit,
623 LolFilter* filter,
624 Handle<Object> error) = 0;
625 };
626
627
628 class LolDumpWriter: public DumpWriter {
629 public:
630 LolDumpWriter(LiveObjectList* older, LiveObjectList* newer)
631 : older_(older), newer_(newer) {
632 }
633
634 void ComputeTotalCountAndSize(LolFilter* filter, int* count, int* size) {
635 *count = 0;
636 *size = 0;
637
638 LolIterator it(older_, newer_);
639 for (it.Init(); !it.Done(); it.Next()) {
640 HeapObject* heap_obj = it.Obj();
641 if (!filter->Matches(heap_obj)) {
642 continue;
643 }
644
645 *size += heap_obj->Size();
646 (*count)++;
647 }
648 }
649
650 bool Write(Handle<FixedArray> elements_arr,
651 int start,
652 int dump_limit,
653 LolFilter* filter,
654 Handle<Object> error) {
655 // The lols are listed in latest to earliest. We want to dump from
656 // earliest to latest. So, compute the last element to start with.
657 int index = 0;
658 int count = 0;
659
660 // Prefetch some needed symbols.
661 Handle<String> id_sym = Factory::LookupAsciiSymbol("id");
662 Handle<String> desc_sym = Factory::LookupAsciiSymbol("desc");
663 Handle<String> size_sym = Factory::LookupAsciiSymbol("size");
664
665 // Fill the array with the lol object details.
666 Handle<JSObject> detail;
667 Handle<String> desc;
668 Handle<HeapObject> target;
669
670 LiveObjectList* first_lol = (older_ != NULL) ?
671 older_->next_ : LiveObjectList::first_;
672 LiveObjectList* last_lol = (newer_ != NULL) ? newer_->next_ : NULL;
673
674 LolForwardIterator it(first_lol, last_lol);
675 for (it.Init(); !it.Done() && (index < dump_limit); it.Next()) {
676 HeapObject* heap_obj = it.Obj();
677
678 // Skip objects that have been filtered out.
679 if (!filter->Matches(heap_obj)) {
680 continue;
681 }
682
683 // Only report objects that are in the section of interest.
684 if (count >= start) {
685 target = Handle<HeapObject>(heap_obj);
686 bool success = AddObjDetail(elements_arr,
687 index++,
688 it.Id(),
689 target,
690 NULL,
691 id_sym,
692 desc_sym,
693 size_sym,
694 detail,
695 desc,
696 error);
697 if (!success) return false;
698 }
699 count++;
700 }
701 return true;
702 }
703
704 private:
705 LiveObjectList* older_;
706 LiveObjectList* newer_;
707 };
708
709
710 class RetainersDumpWriter: public DumpWriter {
711 public:
712 RetainersDumpWriter(Handle<HeapObject> target,
713 Handle<JSObject> instance_filter,
714 Handle<JSFunction> args_function)
715 : target_(target),
716 instance_filter_(instance_filter),
717 args_function_(args_function) {
718 }
719
720 void ComputeTotalCountAndSize(LolFilter* filter, int* count, int* size) {
721 Handle<FixedArray> retainers_arr;
722 Handle<Object> error;
723
724 *size = -1;
725 LiveObjectList::GetRetainers(target_,
726 instance_filter_,
727 retainers_arr,
728 0,
729 Smi::kMaxValue,
730 count,
731 filter,
732 NULL,
733 *args_function_,
734 error);
735 }
736
737 bool Write(Handle<FixedArray> elements_arr,
738 int start,
739 int dump_limit,
740 LolFilter* filter,
741 Handle<Object> error) {
742 int dummy;
743 int count;
744
745 // Fill the retainer objects.
746 count = LiveObjectList::GetRetainers(target_,
747 instance_filter_,
748 elements_arr,
749 start,
750 dump_limit,
751 &dummy,
752 filter,
753 NULL,
754 *args_function_,
755 error);
756 if (count < 0) {
757 return false;
758 }
759 return true;
760 }
761
762 private:
763 Handle<HeapObject> target_;
764 Handle<JSObject> instance_filter_;
765 Handle<JSFunction> args_function_;
766 };
767
768
769 class LiveObjectSummary {
770 public:
771 explicit LiveObjectSummary(LolFilter* filter)
772 : total_count_(0),
773 total_size_(0),
774 found_root_(false),
775 found_weak_root_(false),
776 filter_(filter) {
777 memset(counts_, 0, sizeof(counts_[0]) * kNumberOfEntries);
778 memset(sizes_, 0, sizeof(sizes_[0]) * kNumberOfEntries);
779 }
780
781 void Add(HeapObject* heap_obj) {
782 int size = heap_obj->Size();
783 LiveObjectType type = GetObjectType(heap_obj);
784 ASSERT(type != kInvalidLiveObjType);
785 counts_[type]++;
786 sizes_[type] += size;
787 total_count_++;
788 total_size_ += size;
789 }
790
791 void set_found_root() { found_root_ = true; }
792 void set_found_weak_root() { found_weak_root_ = true; }
793
794 inline int Count(LiveObjectType type) {
795 return counts_[type];
796 }
797 inline int Size(LiveObjectType type) {
798 return sizes_[type];
799 }
800 inline int total_count() {
801 return total_count_;
802 }
803 inline int total_size() {
804 return total_size_;
805 }
806 inline bool found_root() {
807 return found_root_;
808 }
809 inline bool found_weak_root() {
810 return found_weak_root_;
811 }
812 int GetNumberOfEntries() {
813 int entries = 0;
814 for (int i = 0; i < kNumberOfEntries; i++) {
815 if (counts_[i]) entries++;
816 }
817 return entries;
818 }
819
820 inline LolFilter* filter() { return filter_; }
821
822 static const int kNumberOfEntries = kNumberOfTypes;
823
824 private:
825 int counts_[kNumberOfEntries];
826 int sizes_[kNumberOfEntries];
827 int total_count_;
828 int total_size_;
829 bool found_root_;
830 bool found_weak_root_;
831
832 LolFilter *filter_;
833 };
834
835
836 // Abstraction for a summary writer.
837 class SummaryWriter {
838 public:
839 virtual ~SummaryWriter() {}
840 virtual void Write(LiveObjectSummary* summary) = 0;
841 };
842
843
844 // A summary writer for filling in a summary of lol lists and diffs.
845 class LolSummaryWriter: public SummaryWriter {
846 public:
847 LolSummaryWriter(LiveObjectList *older_lol,
848 LiveObjectList *newer_lol)
849 : older_(older_lol), newer_(newer_lol) {
850 }
851
852 void Write(LiveObjectSummary* summary) {
853 LolFilter* filter = summary->filter();
854
855 // Fill the summary with the lol object details.
856 LolIterator it(older_, newer_);
857 for (it.Init(); !it.Done(); it.Next()) {
858 HeapObject* heap_obj = it.Obj();
859 if (!filter->Matches(heap_obj)) {
860 continue;
861 }
862 summary->Add(heap_obj);
863 }
864 }
865
866 private:
867 LiveObjectList* older_;
868 LiveObjectList* newer_;
869 };
870
871
872 // A summary writer for filling in a retainers list.
873 class RetainersSummaryWriter: public SummaryWriter {
874 public:
875 RetainersSummaryWriter(Handle<HeapObject> target,
876 Handle<JSObject> instance_filter,
877 Handle<JSFunction> args_function)
878 : target_(target),
879 instance_filter_(instance_filter),
880 args_function_(args_function) {
881 }
882
883 void Write(LiveObjectSummary* summary) {
884 Handle<FixedArray> retainers_arr;
885 Handle<Object> error;
886 int dummy_total_count;
887 LiveObjectList::GetRetainers(target_,
888 instance_filter_,
889 retainers_arr,
890 0,
891 Smi::kMaxValue,
892 &dummy_total_count,
893 summary->filter(),
894 summary,
895 *args_function_,
896 error);
897 }
898
899 private:
900 Handle<HeapObject> target_;
901 Handle<JSObject> instance_filter_;
902 Handle<JSFunction> args_function_;
903 };
904
905
906 uint32_t LiveObjectList::next_element_id_ = 1;
907 int LiveObjectList::list_count_ = 0;
908 int LiveObjectList::last_id_ = 0;
909 LiveObjectList* LiveObjectList::first_ = NULL;
910 LiveObjectList* LiveObjectList::last_ = NULL;
911
912
913 LiveObjectList::LiveObjectList(LiveObjectList* prev, int capacity)
914 : prev_(prev),
915 next_(NULL),
916 capacity_(capacity),
917 obj_count_(0) {
918 elements_ = NewArray<Element>(capacity);
919 id_ = ++last_id_;
920
921 list_count_++;
922 }
923
924
925 LiveObjectList::~LiveObjectList() {
926 DeleteArray<Element>(elements_);
927 delete prev_;
928 }
929
930
931 int LiveObjectList::GetTotalObjCountAndSize(int* size_p) {
932 int size = 0;
933 int count = 0;
934 LiveObjectList *lol = this;
935 do {
936 // Only compute total size if requested i.e. when size_p is not null.
937 if (size_p != NULL) {
938 Element* elements = lol->elements_;
939 for (int i = 0; i < lol->obj_count_; i++) {
940 HeapObject* heap_obj = elements[i].obj_;
941 size += heap_obj->Size();
942 }
943 }
944 count += lol->obj_count_;
945 lol = lol->prev_;
946 } while (lol != NULL);
947
948 if (size_p != NULL) {
949 *size_p = size;
950 }
951 return count;
952 }
953
954
955 // Adds an object to the lol.
956 // Returns true if successful, else returns false.
957 bool LiveObjectList::Add(HeapObject* obj) {
958 // If the object is already accounted for in the prev list which we inherit
959 // from, then no need to add it to this list.
960 if ((prev() != NULL) && (prev()->Find(obj) != NULL)) {
961 return true;
962 }
963 ASSERT(obj_count_ <= capacity_);
964 if (obj_count_ == capacity_) {
965 // The heap must have grown and we have more objects than capacity to store
966 // them.
967 return false; // Fail this addition.
968 }
969 Element& element = elements_[obj_count_++];
970 element.id_ = next_element_id_++;
971 element.obj_ = obj;
972 return true;
973 }
974
975
976 // Comparator used for sorting and searching the lol.
977 int LiveObjectList::CompareElement(const Element* a, const Element* b) {
978 const HeapObject* obj1 = a->obj_;
979 const HeapObject* obj2 = b->obj_;
980 // For lol elements, it doesn't matter which comes first if 2 elements point
981 // to the same object (which gets culled later). Hence, we only care about
982 // the the greater than / less than relationships.
983 return (obj1 > obj2) ? 1 : (obj1 == obj2) ? 0 : -1;
984 }
985
986
987 // Looks for the specified object in the lol, and returns its element if found.
988 LiveObjectList::Element* LiveObjectList::Find(HeapObject* obj) {
989 LiveObjectList* lol = this;
990 Element key;
991 Element* result = NULL;
992
993 key.obj_ = obj;
994 // Iterate through the chain of lol's to look for the object.
995 while ((result == NULL) && (lol != NULL)) {
996 result = reinterpret_cast<Element*>(
997 bsearch(&key, lol->elements_, lol->obj_count_,
998 sizeof(Element),
999 reinterpret_cast<RawComparer>(CompareElement)));
1000 lol = lol->prev_;
1001 }
1002 return result;
1003 }
1004
1005
1006 // "Nullifies" (convert the HeapObject* into an SMI) so that it will get cleaned
1007 // up in the GCEpilogue, while preserving the sort order of the lol.
1008 // NOTE: the lols need to be already sorted before NullifyMostRecent() is
1009 // called.
1010 void LiveObjectList::NullifyMostRecent(HeapObject* obj) {
1011 LiveObjectList* lol = last();
1012 Element key;
1013 Element* result = NULL;
1014
1015 key.obj_ = obj;
1016 // Iterate through the chain of lol's to look for the object.
1017 while (lol != NULL) {
1018 result = reinterpret_cast<Element*>(
1019 bsearch(&key, lol->elements_, lol->obj_count_,
1020 sizeof(Element),
1021 reinterpret_cast<RawComparer>(CompareElement)));
1022 if (result != NULL) {
1023 // Since there may be more than one (we are nullifying dup's after all),
1024 // find the first in the current lol, and nullify that. The lol should
1025 // be sorted already to make this easy (see the use of SortAll()).
1026 int i = result - lol->elements_;
1027
1028 // NOTE: we sort the lol in increasing order. So, if an object has been
1029 // "nullified" (its lowest bit will be cleared to make it look like an
1030 // SMI), it would/should show up before the equivalent dups that have not
1031 // yet been "nullified". Hence, we should be searching backwards for the
1032 // first occurence of a matching object and nullify that instance. This
1033 // will ensure that we preserve the expected sorting order.
1034 for (i--; i > 0; i--) {
1035 Element* element = &lol->elements_[i];
1036 HeapObject* curr_obj = element->obj_;
1037 if (curr_obj != obj) {
1038 break; // No more matches. Let's move on.
1039 }
1040 result = element; // Let this earlier match be the result.
1041 }
1042
1043 // Nullify the object.
1044 NullifyNonLivePointer(&result->obj_);
1045 return;
1046 }
1047 lol = lol->prev_;
1048 }
1049 }
1050
1051
1052 // Sorts the lol.
1053 void LiveObjectList::Sort() {
1054 if (obj_count_ > 0) {
1055 Vector<Element> elements_v(elements_, obj_count_);
1056 elements_v.Sort(CompareElement);
1057 }
1058 }
1059
1060
1061 // Sorts all captured lols starting from the latest.
1062 void LiveObjectList::SortAll() {
1063 LiveObjectList* lol = last();
1064 while (lol != NULL) {
1065 lol->Sort();
1066 lol = lol->prev_;
1067 }
1068 }
1069
1070
1071 // Counts the number of objects in the heap.
1072 static int CountHeapObjects() {
1073 int count = 0;
1074 // Iterate over all the heap spaces and count the number of objects.
1075 HeapIterator iterator(HeapIterator::kFilterFreeListNodes);
1076 HeapObject* heap_obj = NULL;
1077 while ((heap_obj = iterator.next()) != NULL) {
1078 count++;
1079 }
1080 return count;
1081 }
1082
1083
1084 // Captures a current snapshot of all objects in the heap.
1085 MaybeObject* LiveObjectList::Capture() {
1086 HandleScope scope;
1087
1088 // Count the number of objects in the heap.
1089 int total_count = CountHeapObjects();
1090 int count = total_count;
1091 int size = 0;
1092
1093 LiveObjectList* last_lol = last();
1094 if (last_lol != NULL) {
1095 count -= last_lol->TotalObjCount();
1096 }
1097
1098 LiveObjectList* lol;
1099
1100 // Create a lol large enough to track all the objects.
1101 lol = new LiveObjectList(last_lol, count);
1102 if (lol == NULL) {
1103 return NULL; // No memory to proceed.
1104 }
1105
1106 // The HeapIterator needs to be in its own scope because it disables
1107 // allocation, and we need allocate below.
1108 {
1109 // Iterate over all the heap spaces and add the objects.
1110 HeapIterator iterator(HeapIterator::kFilterFreeListNodes);
1111 HeapObject* heap_obj = NULL;
1112 bool failed = false;
1113 while (!failed && (heap_obj = iterator.next()) != NULL) {
1114 failed = !lol->Add(heap_obj);
1115 size += heap_obj->Size();
1116 }
1117 ASSERT(!failed);
1118
1119 lol->Sort();
1120
1121 // Add the current lol to the list of lols.
1122 if (last_ != NULL) {
1123 last_->next_ = lol;
1124 } else {
1125 first_ = lol;
1126 }
1127 last_ = lol;
1128
1129 #ifdef VERIFY_LOL
1130 if (FLAG_verify_lol) {
1131 Verify(true);
1132 }
1133 #endif
1134 }
1135
1136 Handle<String> id_sym = Factory::LookupAsciiSymbol("id");
1137 Handle<String> count_sym = Factory::LookupAsciiSymbol("count");
1138 Handle<String> size_sym = Factory::LookupAsciiSymbol("size");
1139
1140 Handle<JSObject> result = Factory::NewJSObject(Top::object_function());
1141 if (result->IsFailure()) return Object::cast(*result);
1142
1143 { MaybeObject* maybe_result =
1144 result->SetProperty(*id_sym, Smi::FromInt(lol->id()), NONE);
1145 if (maybe_result->IsFailure()) return maybe_result;
1146 }
1147 { MaybeObject* maybe_result =
1148 result->SetProperty(*count_sym, Smi::FromInt(total_count), NONE);
1149 if (maybe_result->IsFailure()) return maybe_result;
1150 }
1151 { MaybeObject* maybe_result =
1152 result->SetProperty(*size_sym, Smi::FromInt(size), NONE);
1153 if (maybe_result->IsFailure()) return maybe_result;
1154 }
1155
1156 return *result;
1157 }
1158
1159
1160 // Delete doesn't actually deletes an lol. It just marks it as invisible since
1161 // its contents are considered to be part of subsequent lists as well. The
1162 // only time we'll actually delete the lol is when we Reset() or if the lol is
1163 // invisible, and its element count reaches 0.
1164 bool LiveObjectList::Delete(int id) {
1165 LiveObjectList *lol = last();
1166 while (lol != NULL) {
1167 if (lol->id() == id) {
1168 break;
1169 }
1170 lol = lol->prev_;
1171 }
1172
1173 // If no lol is found for this id, then we fail to delete.
1174 if (lol == NULL) return false;
1175
1176 // Else, mark the lol as invisible i.e. id == 0.
1177 lol->id_ = 0;
1178 list_count_--;
1179 ASSERT(list_count_ >= 0);
1180 if (lol->obj_count_ == 0) {
1181 // Point the next lol's prev to this lol's prev.
1182 LiveObjectList* next = lol->next_;
1183 LiveObjectList* prev = lol->prev_;
1184 // Point next's prev to prev.
1185 if (next != NULL) {
1186 next->prev_ = lol->prev_;
1187 } else {
1188 last_ = lol->prev_;
1189 }
1190 // Point prev's next to next.
1191 if (prev != NULL) {
1192 prev->next_ = lol->next_;
1193 } else {
1194 first_ = lol->next_;
1195 }
1196
1197 lol->prev_ = NULL;
1198 lol->next_ = NULL;
1199
1200 // Delete this now empty and invisible lol.
1201 delete lol;
1202 }
1203
1204 // Just in case we've marked everything invisible, then clean up completely.
1205 if (list_count_ == 0) {
1206 Reset();
1207 }
1208
1209 return true;
1210 }
1211
1212
1213 MaybeObject* LiveObjectList::Dump(int older_id,
1214 int newer_id,
1215 int start_idx,
1216 int dump_limit,
1217 Handle<JSObject> filter_obj) {
1218 if ((older_id < 0) || (newer_id < 0) || (last() == NULL)) {
1219 return Failure::Exception(); // Fail: 0 is not a valid lol id.
1220 }
1221 if (newer_id < older_id) {
1222 // They are not in the expected order. Swap them.
1223 int temp = older_id;
1224 older_id = newer_id;
1225 newer_id = temp;
1226 }
1227
1228 LiveObjectList *newer_lol = FindLolForId(newer_id, last());
1229 LiveObjectList *older_lol = FindLolForId(older_id, newer_lol);
1230
1231 // If the id is defined, and we can't find a LOL for it, then we have an
1232 // invalid id.
1233 if ((newer_id != 0) && (newer_lol == NULL)) {
1234 return Failure::Exception(); // Fail: the newer lol id is invalid.
1235 }
1236 if ((older_id != 0) && (older_lol == NULL)) {
1237 return Failure::Exception(); // Fail: the older lol id is invalid.
1238 }
1239
1240 LolFilter filter(filter_obj);
1241 LolDumpWriter writer(older_lol, newer_lol);
1242 return DumpPrivate(&writer, start_idx, dump_limit, &filter);
1243 }
1244
1245
1246 MaybeObject* LiveObjectList::DumpPrivate(DumpWriter* writer,
1247 int start,
1248 int dump_limit,
1249 LolFilter* filter) {
1250 HandleScope scope;
1251
1252 // Calculate the number of entries of the dump.
1253 int count = -1;
1254 int size = -1;
1255 writer->ComputeTotalCountAndSize(filter, &count, &size);
1256
1257 // Adjust for where to start the dump.
1258 if ((start < 0) || (start >= count)) {
1259 return Failure::Exception(); // invalid start.
1260 }
1261
1262 int remaining_count = count - start;
1263 if (dump_limit > remaining_count) {
1264 dump_limit = remaining_count;
1265 }
1266
1267 // Allocate an array to hold the result.
1268 Handle<FixedArray> elements_arr = Factory::NewFixedArray(dump_limit);
1269 if (elements_arr->IsFailure()) return Object::cast(*elements_arr);
1270
1271 // Fill in the dump.
1272 Handle<Object> error;
1273 bool success = writer->Write(elements_arr,
1274 start,
1275 dump_limit,
1276 filter,
1277 error);
1278 if (!success) return Object::cast(*error);
1279
1280 MaybeObject* maybe_result;
1281
1282 // Allocate the result body.
1283 Handle<JSObject> body = Factory::NewJSObject(Top::object_function());
1284 if (body->IsFailure()) return Object::cast(*body);
1285
1286 // Set the updated body.count.
1287 Handle<String> count_sym = Factory::LookupAsciiSymbol("count");
1288 maybe_result = body->SetProperty(*count_sym, Smi::FromInt(count), NONE);
1289 if (maybe_result->IsFailure()) return maybe_result;
1290
1291 // Set the updated body.size if appropriate.
1292 if (size >= 0) {
1293 Handle<String> size_sym = Factory::LookupAsciiSymbol("size");
1294 maybe_result = body->SetProperty(*size_sym, Smi::FromInt(size), NONE);
1295 if (maybe_result->IsFailure()) return maybe_result;
1296 }
1297
1298 // Set body.first_index.
1299 Handle<String> first_sym = Factory::LookupAsciiSymbol("first_index");
1300 maybe_result = body->SetProperty(*first_sym, Smi::FromInt(start), NONE);
1301 if (maybe_result->IsFailure()) return maybe_result;
1302
1303 // Allocate the JSArray of the elements.
1304 Handle<JSObject> elements = Factory::NewJSObject(Top::array_function());
1305 if (elements->IsFailure()) return Object::cast(*elements);
1306 Handle<JSArray>::cast(elements)->SetContent(*elements_arr);
1307
1308 // Set body.elements.
1309 Handle<String> elements_sym = Factory::LookupAsciiSymbol("elements");
1310 maybe_result = body->SetProperty(*elements_sym, *elements, NONE);
1311 if (maybe_result->IsFailure()) return maybe_result;
1312
1313 return *body;
1314 }
1315
1316
1317 MaybeObject* LiveObjectList::Summarize(int older_id,
1318 int newer_id,
1319 Handle<JSObject> filter_obj) {
1320 if ((older_id < 0) || (newer_id < 0) || (last() == NULL)) {
1321 return Failure::Exception(); // Fail: 0 is not a valid lol id.
1322 }
1323 if (newer_id < older_id) {
1324 // They are not in the expected order. Swap them.
1325 int temp = older_id;
1326 older_id = newer_id;
1327 newer_id = temp;
1328 }
1329
1330 LiveObjectList *newer_lol = FindLolForId(newer_id, last());
1331 LiveObjectList *older_lol = FindLolForId(older_id, newer_lol);
1332
1333 // If the id is defined, and we can't find a LOL for it, then we have an
1334 // invalid id.
1335 if ((newer_id != 0) && (newer_lol == NULL)) {
1336 return Failure::Exception(); // Fail: the newer lol id is invalid.
1337 }
1338 if ((older_id != 0) && (older_lol == NULL)) {
1339 return Failure::Exception(); // Fail: the older lol id is invalid.
1340 }
1341
1342 LolFilter filter(filter_obj);
1343 LolSummaryWriter writer(older_lol, newer_lol);
1344 return SummarizePrivate(&writer, &filter, false);
1345 }
1346
1347
1348 // Creates a summary report for the debugger.
1349 // Note: the SummaryWriter takes care of iterating over objects and filling in
1350 // the summary.
1351 MaybeObject* LiveObjectList::SummarizePrivate(SummaryWriter* writer,
1352 LolFilter* filter,
1353 bool is_tracking_roots) {
1354 HandleScope scope;
1355 MaybeObject* maybe_result;
1356
1357 LiveObjectSummary summary(filter);
1358 writer->Write(&summary);
1359
1360 // The result body will look like this:
1361 // body: {
1362 // count: <total_count>,
1363 // size: <total_size>,
1364 // found_root: <boolean>, // optional.
1365 // found_weak_root: <boolean>, // optional.
1366 // summary: [
1367 // {
1368 // desc: "<object type name>",
1369 // count: <count>,
1370 // size: size
1371 // },
1372 // ...
1373 // ]
1374 // }
1375
1376 // Prefetch some needed symbols.
1377 Handle<String> desc_sym = Factory::LookupAsciiSymbol("desc");
1378 Handle<String> count_sym = Factory::LookupAsciiSymbol("count");
1379 Handle<String> size_sym = Factory::LookupAsciiSymbol("size");
1380 Handle<String> summary_sym = Factory::LookupAsciiSymbol("summary");
1381
1382 // Allocate the summary array.
1383 int entries_count = summary.GetNumberOfEntries();
1384 Handle<FixedArray> summary_arr =
1385 Factory::NewFixedArray(entries_count);
1386 if (summary_arr->IsFailure()) return Object::cast(*summary_arr);
1387
1388 int idx = 0;
1389 for (int i = 0; i < LiveObjectSummary::kNumberOfEntries; i++) {
1390 // Allocate the summary record.
1391 Handle<JSObject> detail = Factory::NewJSObject(Top::object_function());
1392 if (detail->IsFailure()) return Object::cast(*detail);
1393
1394 // Fill in the summary record.
1395 LiveObjectType type = static_cast<LiveObjectType>(i);
1396 int count = summary.Count(type);
1397 if (count) {
1398 const char* desc_cstr = GetObjectTypeDesc(type);
1399 Handle<String> desc = Factory::LookupAsciiSymbol(desc_cstr);
1400 int size = summary.Size(type);
1401
1402 maybe_result = detail->SetProperty(*desc_sym, *desc, NONE);
1403 if (maybe_result->IsFailure()) return maybe_result;
1404 maybe_result = detail->SetProperty(*count_sym, Smi::FromInt(count), NONE);
1405 if (maybe_result->IsFailure()) return maybe_result;
1406 maybe_result = detail->SetProperty(*size_sym, Smi::FromInt(size), NONE);
1407 if (maybe_result->IsFailure()) return maybe_result;
1408
1409 summary_arr->set(idx++, *detail);
1410 }
1411 }
1412
1413 // Wrap the summary fixed array in a JS array.
1414 Handle<JSObject> summary_obj = Factory::NewJSObject(Top::array_function());
1415 if (summary_obj->IsFailure()) return Object::cast(*summary_obj);
1416 Handle<JSArray>::cast(summary_obj)->SetContent(*summary_arr);
1417
1418 // Create the body object.
1419 Handle<JSObject> body = Factory::NewJSObject(Top::object_function());
1420 if (body->IsFailure()) return Object::cast(*body);
1421
1422 // Fill out the body object.
1423 int total_count = summary.total_count();
1424 int total_size = summary.total_size();
1425 maybe_result =
1426 body->SetProperty(*count_sym, Smi::FromInt(total_count), NONE);
1427 if (maybe_result->IsFailure()) return maybe_result;
1428
1429 maybe_result = body->SetProperty(*size_sym, Smi::FromInt(total_size), NONE);
1430 if (maybe_result->IsFailure()) return maybe_result;
1431
1432 if (is_tracking_roots) {
1433 int found_root = summary.found_root();
1434 int found_weak_root = summary.found_weak_root();
1435 Handle<String> root_sym = Factory::LookupAsciiSymbol("found_root");
1436 Handle<String> weak_root_sym =
1437 Factory::LookupAsciiSymbol("found_weak_root");
1438 maybe_result =
1439 body->SetProperty(*root_sym, Smi::FromInt(found_root), NONE);
1440 if (maybe_result->IsFailure()) return maybe_result;
1441 maybe_result =
1442 body->SetProperty(*weak_root_sym, Smi::FromInt(found_weak_root), NONE);
1443 if (maybe_result->IsFailure()) return maybe_result;
1444 }
1445
1446 maybe_result = body->SetProperty(*summary_sym, *summary_obj, NONE);
1447 if (maybe_result->IsFailure()) return maybe_result;
1448
1449 return *body;
1450 }
1451
1452
1453 // Returns an array listing the captured lols.
1454 // Note: only dumps the section starting at start_idx and only up to
1455 // dump_limit entries.
1456 MaybeObject* LiveObjectList::Info(int start_idx, int dump_limit) {
1457 HandleScope scope;
1458 MaybeObject* maybe_result;
1459
1460 int total_count = LiveObjectList::list_count();
1461 int dump_count = total_count;
1462
1463 // Adjust for where to start the dump.
1464 if (total_count == 0) {
1465 start_idx = 0; // Ensure this to get an empty list.
1466 } else if ((start_idx < 0) || (start_idx >= total_count)) {
1467 return Failure::Exception(); // invalid start.
1468 }
1469 dump_count -= start_idx;
1470
1471 // Adjust for the dump limit.
1472 if (dump_count > dump_limit) {
1473 dump_count = dump_limit;
1474 }
1475
1476 // Allocate an array to hold the result.
1477 Handle<FixedArray> list = Factory::NewFixedArray(dump_count);
1478 if (list->IsFailure()) return Object::cast(*list);
1479
1480 // Prefetch some needed symbols.
1481 Handle<String> id_sym = Factory::LookupAsciiSymbol("id");
1482 Handle<String> count_sym = Factory::LookupAsciiSymbol("count");
1483 Handle<String> size_sym = Factory::LookupAsciiSymbol("size");
1484
1485 // Fill the array with the lol details.
1486 int idx = 0;
1487 LiveObjectList* lol = first_;
1488 while ((lol != NULL) && (idx < start_idx)) { // Skip tail entries.
1489 if (lol->id() != 0) {
1490 idx++;
1491 }
1492 lol = lol->next();
1493 }
1494 idx = 0;
1495 while ((lol != NULL) && (dump_limit != 0)) {
1496 if (lol->id() != 0) {
1497 int count;
1498 int size;
1499 count = lol->GetTotalObjCountAndSize(&size);
1500
1501 Handle<JSObject> detail = Factory::NewJSObject(Top::object_function());
1502 if (detail->IsFailure()) return Object::cast(*detail);
1503
1504 maybe_result =
1505 detail->SetProperty(*id_sym, Smi::FromInt(lol->id()), NONE);
1506 if (maybe_result->IsFailure()) return maybe_result;
1507 maybe_result =
1508 detail->SetProperty(*count_sym, Smi::FromInt(count), NONE);
1509 if (maybe_result->IsFailure()) return maybe_result;
1510 maybe_result = detail->SetProperty(*size_sym, Smi::FromInt(size), NONE);
1511 if (maybe_result->IsFailure()) return maybe_result;
1512 list->set(idx++, *detail);
1513 dump_limit--;
1514 }
1515 lol = lol->next();
1516 }
1517
1518 // Return the result as a JS array.
1519 Handle<JSObject> lols = Factory::NewJSObject(Top::array_function());
1520 Handle<JSArray>::cast(lols)->SetContent(*list);
1521
1522 Handle<JSObject> result = Factory::NewJSObject(Top::object_function());
1523 if (result->IsFailure()) return Object::cast(*result);
1524
1525 maybe_result =
1526 result->SetProperty(*count_sym, Smi::FromInt(total_count), NONE);
1527 if (maybe_result->IsFailure()) return maybe_result;
1528
1529 Handle<String> first_sym = Factory::LookupAsciiSymbol("first_index");
1530 maybe_result =
1531 result->SetProperty(*first_sym, Smi::FromInt(start_idx), NONE);
1532 if (maybe_result->IsFailure()) return maybe_result;
1533
1534 Handle<String> lists_sym = Factory::LookupAsciiSymbol("lists");
1535 maybe_result = result->SetProperty(*lists_sym, *lols, NONE);
1536 if (maybe_result->IsFailure()) return maybe_result;
1537
1538 return *result;
1539 }
1540
1541
1542 // Deletes all captured lols.
1543 void LiveObjectList::Reset() {
1544 LiveObjectList *lol = last();
1545 // Just delete the last. Each lol will delete it's prev automatically.
1546 delete lol;
1547
1548 next_element_id_ = 1;
1549 list_count_ = 0;
1550 last_id_ = 0;
1551 first_ = NULL;
1552 last_ = NULL;
1553 }
1554
1555
1556 // Gets the object for the specified obj id.
1557 Object* LiveObjectList::GetObj(int obj_id) {
1558 Element* element = FindElementFor<int>(GetElementId, obj_id);
1559 if (element != NULL) {
1560 return Object::cast(element->obj_);
1561 }
1562 return Heap::undefined_value();
1563 }
1564
1565
1566 // Gets the obj id for the specified address if valid.
1567 int LiveObjectList::GetObjId(Object* obj) {
1568 // Make a heap object pointer from the address.
1569 HeapObject* hobj = HeapObject::cast(obj);
1570 Element* element = FindElementFor<HeapObject*>(GetElementObj, hobj);
1571 if (element != NULL) {
1572 return element->id_;
1573 }
1574 return 0; // Invalid address.
1575 }
1576
1577
1578 // Gets the obj id for the specified address if valid.
1579 Object* LiveObjectList::GetObjId(Handle<String> address) {
1580 SmartPointer<char> addr_str =
1581 address->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
1582
1583 // Extract the address value from the string.
1584 int value = static_cast<int>(StringToInt(*address, 16));
1585 Object* obj = reinterpret_cast<Object*>(value);
1586 return Smi::FromInt(GetObjId(obj));
1587 }
1588
1589
1590 // Helper class for copying HeapObjects.
1591 class LolVisitor: public ObjectVisitor {
1592 public:
1593
1594 LolVisitor(HeapObject* target, Handle<HeapObject> handle_to_skip)
1595 : target_(target), handle_to_skip_(handle_to_skip), found_(false) {}
1596
1597 void VisitPointer(Object** p) { CheckPointer(p); }
1598
1599 void VisitPointers(Object** start, Object** end) {
1600 // Check all HeapObject pointers in [start, end).
1601 for (Object** p = start; !found() && p < end; p++) CheckPointer(p);
1602 }
1603
1604 inline bool found() const { return found_; }
1605 inline bool reset() { return found_ = false; }
1606
1607 private:
1608 inline void CheckPointer(Object** p) {
1609 Object* object = *p;
1610 if (HeapObject::cast(object) == target_) {
1611 // We may want to skip this handle because the handle may be a local
1612 // handle in a handle scope in one of our callers. Once we return,
1613 // that handle will be popped. Hence, we don't want to count it as
1614 // a root that would have kept the target object alive.
1615 if (!handle_to_skip_.is_null() &&
1616 handle_to_skip_.location() == reinterpret_cast<HeapObject**>(p)) {
1617 return; // Skip this handle.
1618 }
1619 found_ = true;
1620 }
1621 }
1622
1623 HeapObject* target_;
1624 Handle<HeapObject> handle_to_skip_;
1625 bool found_;
1626 };
1627
1628
1629 inline bool AddRootRetainerIfFound(const LolVisitor& visitor,
1630 LolFilter* filter,
1631 LiveObjectSummary *summary,
1632 void (*SetRootFound)(LiveObjectSummary *s),
1633 int start,
1634 int dump_limit,
1635 int* total_count,
1636 Handle<FixedArray> retainers_arr,
1637 int* count,
1638 int* index,
1639 const char* root_name,
1640 Handle<String> id_sym,
1641 Handle<String> desc_sym,
1642 Handle<String> size_sym,
1643 Handle<Object> error) {
1644 HandleScope scope;
1645
1646 // Scratch handles.
1647 Handle<JSObject> detail;
1648 Handle<String> desc;
1649 Handle<HeapObject> retainer;
1650
1651 if (visitor.found()) {
1652 if (!filter->is_active()) {
1653 (*total_count)++;
1654 if (summary) {
1655 SetRootFound(summary);
1656 } else if ((*total_count > start) && ((*index) < dump_limit)) {
1657 (*count)++;
1658 if (!retainers_arr.is_null()) {
1659 return AddObjDetail(retainers_arr,
1660 (*index)++,
1661 0,
1662 retainer,
1663 root_name,
1664 id_sym,
1665 desc_sym,
1666 size_sym,
1667 detail,
1668 desc,
1669 error);
1670 }
1671 }
1672 }
1673 }
1674 return true;
1675 }
1676
1677
1678 inline void SetFoundRoot(LiveObjectSummary *summary) {
1679 summary->set_found_root();
1680 }
1681
1682
1683 inline void SetFoundWeakRoot(LiveObjectSummary *summary) {
1684 summary->set_found_weak_root();
1685 }
1686
1687
1688 int LiveObjectList::GetRetainers(Handle<HeapObject> target,
1689 Handle<JSObject> instance_filter,
1690 Handle<FixedArray> retainers_arr,
1691 int start,
1692 int dump_limit,
1693 int* total_count,
1694 LolFilter* filter,
1695 LiveObjectSummary *summary,
1696 JSFunction* arguments_function,
1697 Handle<Object> error) {
1698 HandleScope scope;
1699
1700 // Scratch handles.
1701 Handle<JSObject> detail;
1702 Handle<String> desc;
1703 Handle<HeapObject> retainer;
1704
1705 // Prefetch some needed symbols.
1706 Handle<String> id_sym = Factory::LookupAsciiSymbol("id");
1707 Handle<String> desc_sym = Factory::LookupAsciiSymbol("desc");
1708 Handle<String> size_sym = Factory::LookupAsciiSymbol("size");
1709
1710 NoHandleAllocation ha;
1711 int count = 0;
1712 int index = 0;
1713 Handle<JSObject> last_obj;
1714
1715 *total_count = 0;
1716
1717 // Iterate roots.
1718 LolVisitor lol_visitor(*target, target);
1719 Heap::IterateStrongRoots(&lol_visitor, VISIT_ALL);
1720 if (!AddRootRetainerIfFound(lol_visitor,
1721 filter,
1722 summary,
1723 SetFoundRoot,
1724 start,
1725 dump_limit,
1726 total_count,
1727 retainers_arr,
1728 &count,
1729 &index,
1730 "<root>",
1731 id_sym,
1732 desc_sym,
1733 size_sym,
1734 error)) {
1735 return -1;
1736 }
1737
1738 lol_visitor.reset();
1739 Heap::IterateWeakRoots(&lol_visitor, VISIT_ALL);
1740 if (!AddRootRetainerIfFound(lol_visitor,
1741 filter,
1742 summary,
1743 SetFoundWeakRoot,
1744 start,
1745 dump_limit,
1746 total_count,
1747 retainers_arr,
1748 &count,
1749 &index,
1750 "<weak root>",
1751 id_sym,
1752 desc_sym,
1753 size_sym,
1754 error)) {
1755 return -1;
1756 }
1757
1758 // Iterate the live object lists.
1759 LolIterator it(NULL, last());
1760 for (it.Init(); !it.Done() && (index < dump_limit); it.Next()) {
1761 HeapObject* heap_obj = it.Obj();
1762
1763 // Only look at all JSObjects.
1764 if (heap_obj->IsJSObject()) {
1765 // Skip context extension objects and argument arrays as these are
1766 // checked in the context of functions using them.
1767 JSObject* obj = JSObject::cast(heap_obj);
1768 if (obj->IsJSContextExtensionObject() ||
1769 obj->map()->constructor() == arguments_function) {
1770 continue;
1771 }
1772
1773 // Check if the JS object has a reference to the object looked for.
1774 if (obj->ReferencesObject(*target)) {
1775 // Check instance filter if supplied. This is normally used to avoid
1776 // references from mirror objects (see Runtime_IsInPrototypeChain).
1777 if (!instance_filter->IsUndefined()) {
1778 Object* V = obj;
1779 while (true) {
1780 Object* prototype = V->GetPrototype();
1781 if (prototype->IsNull()) {
1782 break;
1783 }
1784 if (*instance_filter == prototype) {
1785 obj = NULL; // Don't add this object.
1786 break;
1787 }
1788 V = prototype;
1789 }
1790 }
1791
1792 if (obj != NULL) {
1793 // Skip objects that have been filtered out.
1794 if (filter->Matches(heap_obj)) {
1795 continue;
1796 }
1797
1798 // Valid reference found add to instance array if supplied an update
1799 // count.
1800 last_obj = Handle<JSObject>(obj);
1801 (*total_count)++;
1802
1803 if (summary != NULL) {
1804 summary->Add(heap_obj);
1805 } else if ((*total_count > start) && (index < dump_limit)) {
1806 count++;
1807 if (!retainers_arr.is_null()) {
1808 retainer = Handle<HeapObject>(heap_obj);
1809 bool success = AddObjDetail(retainers_arr,
1810 index++,
1811 it.Id(),
1812 retainer,
1813 NULL,
1814 id_sym,
1815 desc_sym,
1816 size_sym,
1817 detail,
1818 desc,
1819 error);
1820 if (!success) return -1;
1821 }
1822 }
1823 }
1824 }
1825 }
1826 }
1827
1828 // Check for circular reference only. This can happen when the object is only
1829 // referenced from mirrors and has a circular reference in which case the
1830 // object is not really alive and would have been garbage collected if not
1831 // referenced from the mirror.
1832
1833 if (*total_count == 1 && !last_obj.is_null() && *last_obj == *target) {
1834 count = 0;
1835 *total_count = 0;
1836 }
1837
1838 return count;
1839 }
1840
1841
1842 MaybeObject* LiveObjectList::GetObjRetainers(int obj_id,
1843 Handle<JSObject> instance_filter,
1844 bool verbose,
1845 int start,
1846 int dump_limit,
1847 Handle<JSObject> filter_obj) {
1848 HandleScope scope;
1849
1850 // Get the target object.
1851 HeapObject* heap_obj = HeapObject::cast(GetObj(obj_id));
1852 if (heap_obj == Heap::undefined_value()) {
1853 return heap_obj;
1854 }
1855
1856 Handle<HeapObject> target = Handle<HeapObject>(heap_obj);
1857
1858 // Get the constructor function for context extension and arguments array.
1859 JSObject* arguments_boilerplate =
1860 Top::context()->global_context()->arguments_boilerplate();
1861 JSFunction* arguments_function =
1862 JSFunction::cast(arguments_boilerplate->map()->constructor());
1863
1864 Handle<JSFunction> args_function = Handle<JSFunction>(arguments_function);
1865 LolFilter filter(filter_obj);
1866
1867 if (!verbose) {
1868 RetainersSummaryWriter writer(target, instance_filter, args_function);
1869 return SummarizePrivate(&writer, &filter, true);
1870
1871 } else {
1872 RetainersDumpWriter writer(target, instance_filter, args_function);
1873 Object* body_obj;
1874 MaybeObject* maybe_result =
1875 DumpPrivate(&writer, start, dump_limit, &filter);
1876 if (!maybe_result->ToObject(&body_obj)) {
1877 return maybe_result;
1878 }
1879
1880 // Set body.id.
1881 Handle<JSObject> body = Handle<JSObject>(JSObject::cast(body_obj));
1882 Handle<String> id_sym = Factory::LookupAsciiSymbol("id");
1883 maybe_result = body->SetProperty(*id_sym, Smi::FromInt(obj_id), NONE);
1884 if (maybe_result->IsFailure()) return maybe_result;
1885
1886 return *body;
1887 }
1888 }
1889
1890
1891 Object* LiveObjectList::PrintObj(int obj_id) {
1892 Object* obj = GetObj(obj_id);
1893 if (!obj) {
1894 return Heap::undefined_value();
1895 }
1896
1897 EmbeddedVector<char, 128> temp_filename;
1898 static int temp_count = 0;
1899 const char* path_prefix = ".";
1900
1901 if (FLAG_lol_workdir) {
1902 path_prefix = FLAG_lol_workdir;
1903 }
1904 OS::SNPrintF(temp_filename, "%s/lol-print-%d", path_prefix, ++temp_count);
1905
1906 FILE* f = OS::FOpen(temp_filename.start(), "w+");
1907
1908 PrintF(f, "@%d ", LiveObjectList::GetObjId(obj));
1909 #ifdef OBJECT_PRINT
1910 #ifdef INSPECTOR
1911 Inspector::DumpObjectType(f, obj);
1912 #endif // INSPECTOR
1913 PrintF(f, "\n");
1914 obj->Print(f);
1915 #else // !OBJECT_PRINT
1916 obj->ShortPrint(f);
1917 #endif // !OBJECT_PRINT
1918 PrintF(f, "\n");
1919 Flush(f);
1920 fclose(f);
1921
1922 // Create a string from the temp_file.
1923 // Note: the mmapped resource will take care of closing the file.
1924 MemoryMappedExternalResource* resource =
1925 new MemoryMappedExternalResource(temp_filename.start(), true);
1926 if (resource->exists() && !resource->is_empty()) {
1927 ASSERT(resource->IsAscii());
1928 Handle<String> dump_string =
1929 Factory::NewExternalStringFromAscii(resource);
1930 ExternalStringTable::AddString(*dump_string);
1931 return *dump_string;
1932 } else {
1933 delete resource;
1934 }
1935 return Heap::undefined_value();
1936 }
1937
1938
1939 class LolPathTracer: public PathTracer {
1940 public:
1941 LolPathTracer(FILE* out,
1942 Object* search_target,
1943 WhatToFind what_to_find)
1944 : PathTracer(search_target, what_to_find, VISIT_ONLY_STRONG), out_(out) {}
1945
1946 private:
1947 void ProcessResults();
1948
1949 FILE* out_;
1950 };
1951
1952
1953 void LolPathTracer::ProcessResults() {
1954 if (found_target_) {
1955 PrintF(out_, "=====================================\n");
1956 PrintF(out_, "==== Path to object ====\n");
1957 PrintF(out_, "=====================================\n\n");
1958
1959 ASSERT(!object_stack_.is_empty());
1960 Object* prev = NULL;
1961 for (int i = 0, index = 0; i < object_stack_.length(); i++) {
1962 Object* obj = object_stack_[i];
1963
1964 // Skip this object if it is basically the internals of the
1965 // previous object (which would have dumped its details already).
1966 if (prev && prev->IsJSObject() &&
1967 (obj != search_target_)) {
1968 JSObject* jsobj = JSObject::cast(prev);
1969 if (obj->IsFixedArray() &&
1970 jsobj->properties() == FixedArray::cast(obj)) {
1971 // Skip this one because it would have been printed as the
1972 // properties of the last object already.
1973 continue;
1974 } else if (obj->IsHeapObject() &&
1975 jsobj->elements() == HeapObject::cast(obj)) {
1976 // Skip this one because it would have been printed as the
1977 // elements of the last object already.
1978 continue;
1979 }
1980 }
1981
1982 // Print a connecting arrow.
1983 if (i > 0) PrintF(out_, "\n |\n |\n V\n\n");
1984
1985 // Print the object index.
1986 PrintF(out_, "[%d] ", ++index);
1987
1988 // Print the LOL object ID:
1989 int id = LiveObjectList::GetObjId(obj);
1990 if (id > 0) PrintF(out_, "@%d ", id);
1991
1992 #ifdef OBJECT_PRINT
1993 #ifdef INSPECTOR
1994 Inspector::DumpObjectType(out_, obj);
1995 #endif // INSPECTOR
1996 PrintF(out_, "\n");
1997 obj->Print(out_);
1998 #else // !OBJECT_PRINT
1999 obj->ShortPrint(out_);
2000 PrintF(out_, "\n");
2001 #endif // !OBJECT_PRINT
2002 Flush(out_);
2003 }
2004 PrintF(out_, "\n");
2005 PrintF(out_, "=====================================\n\n");
2006 Flush(out_);
2007 }
2008 }
2009
2010
2011 Object* LiveObjectList::GetPathPrivate(HeapObject* obj1, HeapObject* obj2) {
2012 EmbeddedVector<char, 128> temp_filename;
2013 static int temp_count = 0;
2014 const char* path_prefix = ".";
2015
2016 if (FLAG_lol_workdir) {
2017 path_prefix = FLAG_lol_workdir;
2018 }
2019 OS::SNPrintF(temp_filename, "%s/lol-getpath-%d", path_prefix, ++temp_count);
2020
2021 FILE* f = OS::FOpen(temp_filename.start(), "w+");
2022
2023 // Save the previous verbosity.
2024 bool prev_verbosity = FLAG_use_verbose_printer;
2025 FLAG_use_verbose_printer = false;
2026
2027 // Dump the paths.
2028 {
2029 // The tracer needs to be scoped because its usage asserts no allocation,
2030 // and we need to allocate the result string below.
2031 LolPathTracer tracer(f, obj2, LolPathTracer::FIND_FIRST);
2032
2033 bool found = false;
2034 if (obj1 == NULL) {
2035 // Check for ObjectGroups that references this object.
2036 // TODO(mlam): refactor this to be more modular.
2037 {
2038 List<ObjectGroup*>* groups = GlobalHandles::ObjectGroups();
2039 for (int i = 0; i < groups->length(); i++) {
2040 ObjectGroup* group = groups->at(i);
2041 if (group == NULL) continue;
2042
2043 bool found_group = false;
2044 List<Object**>& objects = group->objects_;
2045 for (int j = 0; j < objects.length(); j++) {
2046 Object* object = *objects[j];
2047 HeapObject* hobj = HeapObject::cast(object);
2048 if (obj2 == hobj) {
2049 found_group = true;
2050 break;
2051 }
2052 }
2053
2054 if (found_group) {
2055 PrintF(f,
2056 "obj %p is a member of object group %p {\n",
2057 reinterpret_cast<void*>(obj2),
2058 reinterpret_cast<void*>(group));
2059 for (int j = 0; j < objects.length(); j++) {
2060 Object* object = *objects[j];
2061 if (!object->IsHeapObject()) continue;
2062
2063 HeapObject* hobj = HeapObject::cast(object);
2064 int id = GetObjId(hobj);
2065 if (id != 0) {
2066 PrintF(f, " @%d:", id);
2067 } else {
2068 PrintF(f, " <no id>:");
2069 }
2070
2071 char buffer[512];
2072 GenerateObjectDesc(hobj, buffer, sizeof(buffer));
2073 PrintF(f, " %s", buffer);
2074 if (hobj == obj2) {
2075 PrintF(f, " <===");
2076 }
2077 PrintF(f, "\n");
2078 }
2079 PrintF(f, "}\n");
2080 }
2081 }
2082 }
2083
2084 PrintF(f, "path from roots to obj %p\n", reinterpret_cast<void*>(obj2));
2085 Heap::IterateRoots(&tracer, VISIT_ONLY_STRONG);
2086 found = tracer.found();
2087
2088 if (!found) {
2089 PrintF(f, " No paths found. Checking symbol tables ...\n");
2090 SymbolTable* symbol_table = Heap::raw_unchecked_symbol_table();
2091 tracer.VisitPointers(reinterpret_cast<Object**>(&symbol_table),
2092 reinterpret_cast<Object**>(&symbol_table)+1);
2093 found = tracer.found();
2094 if (!found) {
2095 symbol_table->IteratePrefix(&tracer);
2096 found = tracer.found();
2097 }
2098 }
2099
2100 if (!found) {
2101 PrintF(f, " No paths found. Checking weak roots ...\n");
2102 // Check weak refs next.
2103 GlobalHandles::IterateWeakRoots(&tracer);
2104 found = tracer.found();
2105 }
2106
2107 } else {
2108 PrintF(f, "path from obj %p to obj %p:\n",
2109 reinterpret_cast<void*>(obj1), reinterpret_cast<void*>(obj2));
2110 tracer.TracePathFrom(reinterpret_cast<Object**>(&obj1));
2111 found = tracer.found();
2112 }
2113
2114 if (!found) {
2115 PrintF(f, " No paths found\n\n");
2116 }
2117 }
2118
2119 // Flush and clean up the dumped file.
2120 Flush(f);
2121 fclose(f);
2122
2123 // Restore the previous verbosity.
2124 FLAG_use_verbose_printer = prev_verbosity;
2125
2126 // Create a string from the temp_file.
2127 // Note: the mmapped resource will take care of closing the file.
2128 MemoryMappedExternalResource* resource =
2129 new MemoryMappedExternalResource(temp_filename.start(), true);
2130 if (resource->exists() && !resource->is_empty()) {
2131 ASSERT(resource->IsAscii());
2132 Handle<String> path_string =
2133 Factory::NewExternalStringFromAscii(resource);
2134 ExternalStringTable::AddString(*path_string);
2135 return *path_string;
2136 } else {
2137 delete resource;
2138 }
2139 return Heap::undefined_value();
2140 }
2141
2142
2143 Object* LiveObjectList::GetPath(int obj_id1,
2144 int obj_id2,
2145 Handle<JSObject> instance_filter) {
2146 HandleScope scope;
2147
2148 // Get the target object.
2149 HeapObject* obj1 = NULL;
2150 if (obj_id1 != 0) {
2151 obj1 = HeapObject::cast(GetObj(obj_id1));
2152 if (obj1 == Heap::undefined_value()) {
2153 return obj1;
2154 }
2155 }
2156
2157 HeapObject* obj2 = HeapObject::cast(GetObj(obj_id2));
2158 if (obj2 == Heap::undefined_value()) {
2159 return obj2;
2160 }
2161
2162 return GetPathPrivate(obj1, obj2);
2163 }
2164
2165
2166 void LiveObjectList::DoProcessNonLive(HeapObject *obj) {
2167 // We should only be called if we have at least one lol to search.
2168 ASSERT(last() != NULL);
2169 Element* element = last()->Find(obj);
2170 if (element != NULL) {
2171 NullifyNonLivePointer(&element->obj_);
2172 }
2173 }
2174
2175
2176 void LiveObjectList::IterateElementsPrivate(ObjectVisitor* v) {
2177 LiveObjectList* lol = last();
2178 while (lol != NULL) {
2179 Element* elements = lol->elements_;
2180 int count = lol->obj_count_;
2181 for (int i = 0; i < count; i++) {
2182 HeapObject** p = &elements[i].obj_;
2183 v->VisitPointer(reinterpret_cast<Object **>(p));
2184 }
2185 lol = lol->prev_;
2186 }
2187 }
2188
2189
2190 // Purpose: Called by GCEpilogue to purge duplicates. Not to be called by
2191 // anyone else.
2192 void LiveObjectList::PurgeDuplicates() {
2193 bool is_sorted = false;
2194 LiveObjectList* lol = last();
2195 if (!lol) {
2196 return; // Nothing to purge.
2197 }
2198
2199 int total_count = lol->TotalObjCount();
2200 if (!total_count) {
2201 return; // Nothing to purge.
2202 }
2203
2204 Element* elements = NewArray<Element>(total_count);
2205 int count = 0;
2206
2207 // Copy all the object elements into a consecutive array.
2208 while (lol) {
2209 memcpy(&elements[count], lol->elements_, lol->obj_count_ * sizeof(Element));
2210 count += lol->obj_count_;
2211 lol = lol->prev_;
2212 }
2213 qsort(elements, total_count, sizeof(Element),
2214 reinterpret_cast<RawComparer>(CompareElement));
2215
2216 ASSERT(count == total_count);
2217
2218 // Iterate over all objects in the consolidated list and check for dups.
2219 total_count--;
2220 for (int i = 0; i < total_count; ) {
2221 Element* curr = &elements[i];
2222 HeapObject* curr_obj = curr->obj_;
2223 int j = i+1;
2224 bool done = false;
2225
2226 while (!done && (j < total_count)) {
2227 // Process if the element's object is still live after the current GC.
2228 // Non-live objects will be converted to SMIs i.e. not HeapObjects.
2229 if (curr_obj->IsHeapObject()) {
2230 Element* next = &elements[j];
2231 HeapObject* next_obj = next->obj_;
2232 if (next_obj->IsHeapObject()) {
2233 if (curr_obj != next_obj) {
2234 done = true;
2235 continue; // Live object but no match. Move on.
2236 }
2237
2238 // NOTE: we've just GCed the LOLs. Hence, they are no longer sorted.
2239 // Since we detected at least one need to search for entries, we'll
2240 // sort it to enable the use of NullifyMostRecent() below. We only
2241 // need to sort it once (except for one exception ... see below).
2242 if (!is_sorted) {
2243 SortAll();
2244 is_sorted = true;
2245 }
2246
2247 // We have a match. Need to nullify the most recent ref to this
2248 // object. We'll keep the oldest ref:
2249 // Note: we will nullify the element record in the LOL
2250 // database, not in the local sorted copy of the elements.
2251 NullifyMostRecent(curr_obj);
2252 }
2253 }
2254 // Either the object was already marked for purging, or we just marked
2255 // it. Either way, if there's more than one dup, then we need to check
2256 // the next element for another possible dup against the current as well
2257 // before we move on. So, here we go.
2258 j++;
2259 }
2260
2261 // We can move on to checking the match on the next element.
2262 i = j;
2263 }
2264
2265 DeleteArray<Element>(elements);
2266 }
2267
2268
2269 // Purpose: Purges dead objects and resorts the LOLs.
2270 void LiveObjectList::GCEpiloguePrivate() {
2271 // Note: During the GC, ConsStrings may be collected and pointers may be
2272 // forwarded to its constituent string. As a result, we may find dupes of
2273 // objects references in the LOL list.
2274 // Another common way we get dups is that free chunks that have been swept
2275 // in the oldGen heap may be kept as ByteArray objects in a free list.
2276 //
2277 // When we promote live objects from the youngGen, the object may be moved
2278 // to the start of these free chunks. Since there is no free or move event
2279 // for the free chunks, their addresses will show up 2 times: once for their
2280 // original free ByteArray selves, and once for the newly promoted youngGen
2281 // object. Hence, we can get a duplicate address in the LOL again.
2282 //
2283 // We need to eliminate these dups because the LOL implementation expects to
2284 // only have at most one unique LOL reference to any object at any time.
2285 PurgeDuplicates();
2286
2287 // After the GC, sweep away all free'd Elements and compact.
2288 LiveObjectList *prev = NULL;
2289 LiveObjectList *next = NULL;
2290
2291 // Iterating from the youngest lol to the oldest lol.
2292 for (LiveObjectList *lol = last(); lol; lol = prev) {
2293 Element* elements = lol->elements_;
2294 prev = lol->prev(); // Save the prev.
2295
2296 // Remove any references to collected objects.
2297 int i = 0;
2298 while (i < lol->obj_count_) {
2299 Element& element = elements[i];
2300 if (!element.obj_->IsHeapObject()) {
2301 // If the HeapObject address was converted into a SMI, then this
2302 // is a dead object. Copy the last element over this one.
2303 element = elements[lol->obj_count_ - 1];
2304 lol->obj_count_--;
2305 // We've just moved the last element into this index. We'll revisit
2306 // this index again. Hence, no need to increment the iterator.
2307 } else {
2308 i++; // Look at the next element next.
2309 }
2310 }
2311
2312 int new_count = lol->obj_count_;
2313
2314 // Check if there are any more elements to keep after purging the dead ones.
2315 if (new_count == 0) {
2316 DeleteArray<Element>(elements);
2317 lol->elements_ = NULL;
2318 lol->capacity_ = 0;
2319 ASSERT(lol->obj_count_ == 0);
2320
2321 // If the list is also invisible, the clean up the list as well.
2322 if (lol->id_ == 0) {
2323 // Point the next lol's prev to this lol's prev.
2324 if (next) {
2325 next->prev_ = lol->prev_;
2326 } else {
2327 last_ = lol->prev_;
2328 }
2329
2330 // Delete this now empty and invisible lol.
2331 delete lol;
2332
2333 // Don't point the next to this lol since it is now deleted.
2334 // Leave the next pointer pointing to the current lol.
2335 continue;
2336 }
2337
2338 } else {
2339 // If the obj_count_ is less than the capacity and the difference is
2340 // greater than a specified threshold, then we should shrink the list.
2341 int diff = lol->capacity_ - new_count;
2342 const int kMaxUnusedSpace = 64;
2343 if (diff > kMaxUnusedSpace) { // Threshold for shrinking.
2344 // Shrink the list.
2345 Element *new_elements = NewArray<Element>(new_count);
2346 memcpy(new_elements, elements, new_count * sizeof(Element));
2347
2348 DeleteArray<Element>(elements);
2349 lol->elements_ = new_elements;
2350 lol->capacity_ = new_count;
2351 }
2352 ASSERT(lol->obj_count_ == new_count);
2353
2354 lol->Sort(); // We've moved objects. Re-sort in case.
2355 }
2356
2357 // Save the next (for the previous link) in case we need it later.
2358 next = lol;
2359 }
2360
2361 #ifdef VERIFY_LOL
2362 if (FLAG_verify_lol) {
2363 Verify();
2364 }
2365 #endif
2366 }
2367
2368
2369 #ifdef VERIFY_LOL
2370 void LiveObjectList::Verify(bool match_heap_exactly) {
2371 OS::Print("Verifying the LiveObjectList database:\n");
2372
2373 LiveObjectList* lol = last();
2374 if (lol == NULL) {
2375 OS::Print(" No lol database to verify\n");
2376 return;
2377 }
2378
2379 OS::Print(" Preparing the lol database ...\n");
2380 int total_count = lol->TotalObjCount();
2381
2382 Element* elements = NewArray<Element>(total_count);
2383 int count = 0;
2384
2385 // Copy all the object elements into a consecutive array.
2386 OS::Print(" Copying the lol database ...\n");
2387 while (lol != NULL) {
2388 memcpy(&elements[count], lol->elements_, lol->obj_count_ * sizeof(Element));
2389 count += lol->obj_count_;
2390 lol = lol->prev_;
2391 }
2392 qsort(elements, total_count, sizeof(Element),
2393 reinterpret_cast<RawComparer>(CompareElement));
2394
2395 ASSERT(count == total_count);
2396
2397 // Iterate over all objects in the heap and check for:
2398 // 1. object in LOL but not in heap i.e. error.
2399 // 2. object in heap but not in LOL (possibly not an error). Usually
2400 // just means that we don't have the a capture of the latest heap.
2401 // That is unless we did this verify immediately after a capture,
2402 // and specified match_heap_exactly = true.
2403
2404 int number_of_heap_objects = 0;
2405 int number_of_matches = 0;
2406 int number_not_in_heap = total_count;
2407 int number_not_in_lol = 0;
2408
2409 OS::Print(" Start verify ...\n");
2410 OS::Print(" Verifying ...");
2411 Flush();
2412 HeapIterator iterator(HeapIterator::kFilterFreeListNodes);
2413 HeapObject* heap_obj = NULL;
2414 while ((heap_obj = iterator.next()) != NULL) {
2415 number_of_heap_objects++;
2416
2417 // Check if the heap_obj is in the lol.
2418 Element key;
2419 key.obj_ = heap_obj;
2420
2421 Element* result = reinterpret_cast<Element*>(
2422 bsearch(&key, elements, total_count, sizeof(Element),
2423 reinterpret_cast<RawComparer>(CompareElement)));
2424
2425 if (result != NULL) {
2426 number_of_matches++;
2427 number_not_in_heap--;
2428 // Mark it as found by changing it into a SMI (mask off low bit).
2429 // Note: we cannot use HeapObject::cast() here because it asserts that
2430 // the HeapObject bit is set on the address, but we're unsetting it on
2431 // purpose here for our marking.
2432 result->obj_ = reinterpret_cast<HeapObject*>(heap_obj->address());
2433
2434 } else {
2435 number_not_in_lol++;
2436 if (match_heap_exactly) {
2437 OS::Print("heap object %p NOT in lol database\n", heap_obj);
2438 }
2439 }
2440 // Show some sign of life.
2441 if (number_of_heap_objects % 1000 == 0) {
2442 OS::Print(".");
2443 fflush(stdout);
2444 }
2445 }
2446 OS::Print("\n");
2447
2448 // Reporting lol objects not found in the heap.
2449 if (number_not_in_heap) {
2450 int found = 0;
2451 for (int i = 0; (i < total_count) && (found < number_not_in_heap); i++) {
2452 Element& element = elements[i];
2453 if (element.obj_->IsHeapObject()) {
2454 OS::Print("lol database object [%d of %d] %p NOT in heap\n",
2455 i, total_count, element.obj_);
2456 found++;
2457 }
2458 }
2459 }
2460
2461 DeleteArray<Element>(elements);
2462
2463 OS::Print("number of objects in lol database %d\n", total_count);
2464 OS::Print("number of heap objects .......... %d\n", number_of_heap_objects);
2465 OS::Print("number of matches ............... %d\n", number_of_matches);
2466 OS::Print("number NOT in heap .............. %d\n", number_not_in_heap);
2467 OS::Print("number NOT in lol database ...... %d\n", number_not_in_lol);
2468
2469 if (number_of_matches != total_count) {
2470 OS::Print(" *** ERROR: "
2471 "NOT all lol database objects match heap objects.\n");
2472 }
2473 if (number_not_in_heap != 0) {
2474 OS::Print(" *** ERROR: %d lol database objects not found in heap.\n",
2475 number_not_in_heap);
2476 }
2477 if (match_heap_exactly) {
2478 if (!(number_not_in_lol == 0)) {
2479 OS::Print(" *** ERROR: %d heap objects NOT found in lol database.\n",
2480 number_not_in_lol);
2481 }
2482 }
2483
2484 ASSERT(number_of_matches == total_count);
2485 ASSERT(number_not_in_heap == 0);
2486 ASSERT(number_not_in_lol == (number_of_heap_objects - total_count));
2487 if (match_heap_exactly) {
2488 ASSERT(total_count == number_of_heap_objects);
2489 ASSERT(number_not_in_lol == 0);
2490 }
2491
2492 OS::Print(" Verify the lol database is sorted ...\n");
2493 lol = last();
2494 while (lol != NULL) {
2495 Element* elements = lol->elements_;
2496 for (int i = 0; i < lol->obj_count_ - 1; i++) {
2497 if (elements[i].obj_ >= elements[i+1].obj_) {
2498 OS::Print(" *** ERROR: lol %p obj[%d] %p > obj[%d] %p\n",
2499 lol, i, elements[i].obj_, i+1, elements[i+1].obj_);
2500 }
2501 }
2502 lol = lol->prev_;
2503 }
2504
2505 OS::Print(" DONE verifying.\n\n\n");
2506 }
2507
2508
2509 void LiveObjectList::VerifyNotInFromSpace() {
2510 OS::Print("VerifyNotInFromSpace() ...\n");
2511 LolIterator it(NULL, last());
2512 int i = 0;
2513 for (it.Init(); !it.Done(); it.Next()) {
2514 HeapObject* heap_obj = it.Obj();
2515 if (Heap::InFromSpace(heap_obj)) {
2516 OS::Print(" ERROR: VerifyNotInFromSpace: [%d] obj %p in From space %p\n",
2517 i++, heap_obj, Heap::new_space()->FromSpaceLow());
2518 }
2519 }
2520 }
2521 #endif // VERIFY_LOL
2522
49 2523
50 } } // namespace v8::internal 2524 } } // namespace v8::internal
51 2525
52 #endif // LIVE_OBJECT_LIST 2526 #endif // LIVE_OBJECT_LIST
53 2527
OLDNEW
« no previous file with comments | « src/liveobjectlist.h ('k') | src/liveobjectlist-inl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698