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

Side by Side Diff: src/liveobjectlist.cc

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