blob: 451a28ab70fa4e6154b9cd24ac0336117c23bde1 [file] [log] [blame]
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00001// 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"
danno@chromium.orgb6451162011-08-17 14:33:23 +000039#include "isolate.h"
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +000040#include "list-inl.h"
ager@chromium.org9ee27ae2011-03-02 13:43:26 +000041#include "liveobjectlist-inl.h"
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +000042#include "string-stream.h"
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +000043#include "v8utils.h"
danno@chromium.orgb6451162011-08-17 14:33:23 +000044#include "v8conversions.h"
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +000045
46namespace v8 {
47namespace internal {
48
49
ager@chromium.org9ee27ae2011-03-02 13:43:26 +000050typedef 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(SeqAsciiString, "unexpected: SeqAsciiString") \
75 v(SeqString, "unexpected: SeqString") \
76 v(JSFunctionResultCache, "unexpected: JSFunctionResultCache") \
77 v(GlobalContext, "unexpected: GlobalContext") \
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") \
danno@chromium.orgb6451162011-08-17 14:33:23 +0000113 v(ExternalPixelArray, "meta: PixelArray") \
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000114 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") \
ager@chromium.orgea91cc52011-05-23 06:06:11 +0000122 v(Foreign, "meta: Foreign") \
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000123 v(SharedFunctionInfo, "meta: SharedFunctionInfo") \
124 v(Struct, "meta: Struct") \
125 \
126 v(HeapObject, "HeapObject")
127
128
129enum /* 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
138LiveObjectType 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
150inline 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
162const char* GetObjectTypeDesc(HeapObject* heap_obj) {
163 LiveObjectType type = GetObjectType(heap_obj);
164 return GetObjectTypeDesc(type);
165}
166
167
168bool 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
184const AllocationSpace kInvalidSpace = static_cast<AllocationSpace>(-1);
185
186static AllocationSpace FindSpaceFor(String* space_str) {
187 SmartPointer<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
214static bool InSpace(AllocationSpace space, HeapObject *heap_obj) {
danno@chromium.orgb6451162011-08-17 14:33:23 +0000215 Heap* heap = ISOLATE->heap();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000216 if (space != LO_SPACE) {
danno@chromium.orgb6451162011-08-17 14:33:23 +0000217 return heap->InSpace(heap_obj, space);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000218 }
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++) {
danno@chromium.orgb6451162011-08-17 14:33:23 +0000229 if (heap->InSpace(heap_obj, static_cast<AllocationSpace>(sp))) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000230 return false;
231 }
232 }
danno@chromium.orgb6451162011-08-17 14:33:23 +0000233 SLOW_ASSERT(heap->InSpace(heap_obj, LO_SPACE));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000234 return true;
235}
236
237
238static LiveObjectType FindTypeFor(String* type_str) {
239 SmartPointer<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
254class 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
276LolFilter::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
289void LolFilter::InitTypeFilter(Handle<JSObject> filter_obj) {
danno@chromium.orgb6451162011-08-17 14:33:23 +0000290 Handle<String> type_sym = FACTORY->LookupAsciiSymbol("type");
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000291 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
305void LolFilter::InitSpaceFilter(Handle<JSObject> filter_obj) {
danno@chromium.orgb6451162011-08-17 14:33:23 +0000306 Handle<String> space_sym = FACTORY->LookupAsciiSymbol("space");
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000307 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
321void LolFilter::InitPropertyFilter(Handle<JSObject> filter_obj) {
danno@chromium.orgb6451162011-08-17 14:33:23 +0000322 Handle<String> prop_sym = FACTORY->LookupAsciiSymbol("prop");
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000323 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
334bool 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
352class 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
421class 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.
459static 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.
486static 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 SmartPointer<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 SmartPointer<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 SmartPointer<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.
565static 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) {
danno@chromium.orgb6451162011-08-17 14:33:23 +0000576 Isolate* isolate = Isolate::Current();
577 Factory* factory = isolate->factory();
578 detail = factory->NewJSObject(isolate->object_function());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000579 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 }
danno@chromium.orgb6451162011-08-17 14:33:23 +0000593 desc = factory->NewStringFromAscii(CStrVector(desc_str));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000594 if (desc->IsFailure()) {
595 error = desc;
596 return false;
597 }
598
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000599 { MaybeObject* maybe_result = detail->SetProperty(*id_sym,
600 Smi::FromInt(obj_id),
601 NONE,
602 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000603 if (maybe_result->IsFailure()) return false;
604 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000605 { MaybeObject* maybe_result = detail->SetProperty(*desc_sym,
606 *desc,
607 NONE,
608 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000609 if (maybe_result->IsFailure()) return false;
610 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000611 { MaybeObject* maybe_result = detail->SetProperty(*size_sym,
612 Smi::FromInt(size),
613 NONE,
614 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000615 if (maybe_result->IsFailure()) return false;
616 }
617
618 arr->set(index, *detail);
619 return true;
620}
621
622
623class 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
638class 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
danno@chromium.orgb6451162011-08-17 14:33:23 +0000670 Isolate* isolate = Isolate::Current();
671 Factory* factory = isolate->factory();
672
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000673 // Prefetch some needed symbols.
danno@chromium.orgb6451162011-08-17 14:33:23 +0000674 Handle<String> id_sym = factory->LookupAsciiSymbol("id");
675 Handle<String> desc_sym = factory->LookupAsciiSymbol("desc");
676 Handle<String> size_sym = factory->LookupAsciiSymbol("size");
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000677
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
723class 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
782class 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.
850class 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.
858class 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.
886class 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
919uint32_t LiveObjectList::next_element_id_ = 1;
920int LiveObjectList::list_count_ = 0;
921int LiveObjectList::last_id_ = 0;
922LiveObjectList* LiveObjectList::first_ = NULL;
923LiveObjectList* LiveObjectList::last_ = NULL;
924
925
926LiveObjectList::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
938LiveObjectList::~LiveObjectList() {
939 DeleteArray<Element>(elements_);
940 delete prev_;
941}
942
943
944int 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.
970bool 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.
990int 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.
1001LiveObjectList::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.
1023void 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.
1066void 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.
1075void 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.
1085static int CountHeapObjects() {
1086 int count = 0;
1087 // Iterate over all the heap spaces and count the number of objects.
1088 HeapIterator iterator(HeapIterator::kFilterFreeListNodes);
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.
1098MaybeObject* LiveObjectList::Capture() {
danno@chromium.orgb6451162011-08-17 14:33:23 +00001099 Isolate* isolate = Isolate::Current();
1100 Factory* factory = isolate->factory();
1101 HandleScope scope(isolate);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001102
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(HeapIterator::kFilterFreeListNodes);
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
danno@chromium.orgb6451162011-08-17 14:33:23 +00001151 Handle<String> id_sym = factory->LookupAsciiSymbol("id");
1152 Handle<String> count_sym = factory->LookupAsciiSymbol("count");
1153 Handle<String> size_sym = factory->LookupAsciiSymbol("size");
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001154
danno@chromium.orgb6451162011-08-17 14:33:23 +00001155 Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001156 if (result->IsFailure()) return Object::cast(*result);
1157
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001158 { MaybeObject* maybe_result = result->SetProperty(*id_sym,
1159 Smi::FromInt(lol->id()),
1160 NONE,
1161 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001162 if (maybe_result->IsFailure()) return maybe_result;
1163 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001164 { MaybeObject* maybe_result = result->SetProperty(*count_sym,
1165 Smi::FromInt(total_count),
1166 NONE,
1167 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001168 if (maybe_result->IsFailure()) return maybe_result;
1169 }
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001170 { MaybeObject* maybe_result = result->SetProperty(*size_sym,
1171 Smi::FromInt(size),
1172 NONE,
1173 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001174 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.
1185bool 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
1234MaybeObject* 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
1267MaybeObject* LiveObjectList::DumpPrivate(DumpWriter* writer,
1268 int start,
1269 int dump_limit,
1270 LolFilter* filter) {
danno@chromium.orgb6451162011-08-17 14:33:23 +00001271 Isolate* isolate = Isolate::Current();
1272 Factory* factory = isolate->factory();
1273
1274 HandleScope scope(isolate);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001275
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.
danno@chromium.orgb6451162011-08-17 14:33:23 +00001292 Handle<FixedArray> elements_arr = factory->NewFixedArray(dump_limit);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001293 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.
danno@chromium.orgb6451162011-08-17 14:33:23 +00001307 Handle<JSObject> body = factory->NewJSObject(isolate->object_function());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001308 if (body->IsFailure()) return Object::cast(*body);
1309
1310 // Set the updated body.count.
danno@chromium.orgb6451162011-08-17 14:33:23 +00001311 Handle<String> count_sym = factory->LookupAsciiSymbol("count");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001312 maybe_result = body->SetProperty(*count_sym,
1313 Smi::FromInt(count),
1314 NONE,
1315 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001316 if (maybe_result->IsFailure()) return maybe_result;
1317
1318 // Set the updated body.size if appropriate.
1319 if (size >= 0) {
danno@chromium.orgb6451162011-08-17 14:33:23 +00001320 Handle<String> size_sym = factory->LookupAsciiSymbol("size");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001321 maybe_result = body->SetProperty(*size_sym,
1322 Smi::FromInt(size),
1323 NONE,
1324 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001325 if (maybe_result->IsFailure()) return maybe_result;
1326 }
1327
1328 // Set body.first_index.
danno@chromium.orgb6451162011-08-17 14:33:23 +00001329 Handle<String> first_sym = factory->LookupAsciiSymbol("first_index");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001330 maybe_result = body->SetProperty(*first_sym,
1331 Smi::FromInt(start),
1332 NONE,
1333 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001334 if (maybe_result->IsFailure()) return maybe_result;
1335
1336 // Allocate the JSArray of the elements.
danno@chromium.orgb6451162011-08-17 14:33:23 +00001337 Handle<JSObject> elements = factory->NewJSObject(isolate->array_function());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001338 if (elements->IsFailure()) return Object::cast(*elements);
1339 Handle<JSArray>::cast(elements)->SetContent(*elements_arr);
1340
1341 // Set body.elements.
danno@chromium.orgb6451162011-08-17 14:33:23 +00001342 Handle<String> elements_sym = factory->LookupAsciiSymbol("elements");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001343 maybe_result = body->SetProperty(*elements_sym,
1344 *elements,
1345 NONE,
1346 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001347 if (maybe_result->IsFailure()) return maybe_result;
1348
1349 return *body;
1350}
1351
1352
1353MaybeObject* LiveObjectList::Summarize(int older_id,
1354 int newer_id,
1355 Handle<JSObject> filter_obj) {
1356 if ((older_id < 0) || (newer_id < 0) || (last() == NULL)) {
1357 return Failure::Exception(); // Fail: 0 is not a valid lol id.
1358 }
1359 if (newer_id < older_id) {
1360 // They are not in the expected order. Swap them.
1361 int temp = older_id;
1362 older_id = newer_id;
1363 newer_id = temp;
1364 }
1365
1366 LiveObjectList *newer_lol = FindLolForId(newer_id, last());
1367 LiveObjectList *older_lol = FindLolForId(older_id, newer_lol);
1368
1369 // If the id is defined, and we can't find a LOL for it, then we have an
1370 // invalid id.
1371 if ((newer_id != 0) && (newer_lol == NULL)) {
1372 return Failure::Exception(); // Fail: the newer lol id is invalid.
1373 }
1374 if ((older_id != 0) && (older_lol == NULL)) {
1375 return Failure::Exception(); // Fail: the older lol id is invalid.
1376 }
1377
1378 LolFilter filter(filter_obj);
1379 LolSummaryWriter writer(older_lol, newer_lol);
1380 return SummarizePrivate(&writer, &filter, false);
1381}
1382
1383
1384// Creates a summary report for the debugger.
1385// Note: the SummaryWriter takes care of iterating over objects and filling in
1386// the summary.
1387MaybeObject* LiveObjectList::SummarizePrivate(SummaryWriter* writer,
1388 LolFilter* filter,
1389 bool is_tracking_roots) {
1390 HandleScope scope;
1391 MaybeObject* maybe_result;
1392
1393 LiveObjectSummary summary(filter);
1394 writer->Write(&summary);
1395
danno@chromium.orgb6451162011-08-17 14:33:23 +00001396 Isolate* isolate = Isolate::Current();
1397 Factory* factory = isolate->factory();
1398
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001399 // The result body will look like this:
1400 // body: {
1401 // count: <total_count>,
1402 // size: <total_size>,
1403 // found_root: <boolean>, // optional.
1404 // found_weak_root: <boolean>, // optional.
1405 // summary: [
1406 // {
1407 // desc: "<object type name>",
1408 // count: <count>,
1409 // size: size
1410 // },
1411 // ...
1412 // ]
1413 // }
1414
1415 // Prefetch some needed symbols.
danno@chromium.orgb6451162011-08-17 14:33:23 +00001416 Handle<String> desc_sym = factory->LookupAsciiSymbol("desc");
1417 Handle<String> count_sym = factory->LookupAsciiSymbol("count");
1418 Handle<String> size_sym = factory->LookupAsciiSymbol("size");
1419 Handle<String> summary_sym = factory->LookupAsciiSymbol("summary");
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001420
1421 // Allocate the summary array.
1422 int entries_count = summary.GetNumberOfEntries();
1423 Handle<FixedArray> summary_arr =
danno@chromium.orgb6451162011-08-17 14:33:23 +00001424 factory->NewFixedArray(entries_count);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001425 if (summary_arr->IsFailure()) return Object::cast(*summary_arr);
1426
1427 int idx = 0;
1428 for (int i = 0; i < LiveObjectSummary::kNumberOfEntries; i++) {
1429 // Allocate the summary record.
danno@chromium.orgb6451162011-08-17 14:33:23 +00001430 Handle<JSObject> detail = factory->NewJSObject(isolate->object_function());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001431 if (detail->IsFailure()) return Object::cast(*detail);
1432
1433 // Fill in the summary record.
1434 LiveObjectType type = static_cast<LiveObjectType>(i);
1435 int count = summary.Count(type);
1436 if (count) {
1437 const char* desc_cstr = GetObjectTypeDesc(type);
danno@chromium.orgb6451162011-08-17 14:33:23 +00001438 Handle<String> desc = factory->LookupAsciiSymbol(desc_cstr);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001439 int size = summary.Size(type);
1440
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001441 maybe_result = detail->SetProperty(*desc_sym,
1442 *desc,
1443 NONE,
1444 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001445 if (maybe_result->IsFailure()) return maybe_result;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001446 maybe_result = detail->SetProperty(*count_sym,
1447 Smi::FromInt(count),
1448 NONE,
1449 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001450 if (maybe_result->IsFailure()) return maybe_result;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001451 maybe_result = detail->SetProperty(*size_sym,
1452 Smi::FromInt(size),
1453 NONE,
1454 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001455 if (maybe_result->IsFailure()) return maybe_result;
1456
1457 summary_arr->set(idx++, *detail);
1458 }
1459 }
1460
1461 // Wrap the summary fixed array in a JS array.
danno@chromium.orgb6451162011-08-17 14:33:23 +00001462 Handle<JSObject> summary_obj =
1463 factory->NewJSObject(isolate->array_function());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001464 if (summary_obj->IsFailure()) return Object::cast(*summary_obj);
1465 Handle<JSArray>::cast(summary_obj)->SetContent(*summary_arr);
1466
1467 // Create the body object.
danno@chromium.orgb6451162011-08-17 14:33:23 +00001468 Handle<JSObject> body = factory->NewJSObject(isolate->object_function());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001469 if (body->IsFailure()) return Object::cast(*body);
1470
1471 // Fill out the body object.
1472 int total_count = summary.total_count();
1473 int total_size = summary.total_size();
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001474 maybe_result = body->SetProperty(*count_sym,
1475 Smi::FromInt(total_count),
1476 NONE,
1477 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001478 if (maybe_result->IsFailure()) return maybe_result;
1479
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001480 maybe_result = body->SetProperty(*size_sym,
1481 Smi::FromInt(total_size),
1482 NONE,
1483 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001484 if (maybe_result->IsFailure()) return maybe_result;
1485
1486 if (is_tracking_roots) {
1487 int found_root = summary.found_root();
1488 int found_weak_root = summary.found_weak_root();
danno@chromium.orgb6451162011-08-17 14:33:23 +00001489 Handle<String> root_sym = factory->LookupAsciiSymbol("found_root");
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001490 Handle<String> weak_root_sym =
danno@chromium.orgb6451162011-08-17 14:33:23 +00001491 factory->LookupAsciiSymbol("found_weak_root");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001492 maybe_result = body->SetProperty(*root_sym,
1493 Smi::FromInt(found_root),
1494 NONE,
1495 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001496 if (maybe_result->IsFailure()) return maybe_result;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001497 maybe_result = body->SetProperty(*weak_root_sym,
1498 Smi::FromInt(found_weak_root),
1499 NONE,
1500 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001501 if (maybe_result->IsFailure()) return maybe_result;
1502 }
1503
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001504 maybe_result = body->SetProperty(*summary_sym,
1505 *summary_obj,
1506 NONE,
1507 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001508 if (maybe_result->IsFailure()) return maybe_result;
1509
1510 return *body;
1511}
1512
1513
1514// Returns an array listing the captured lols.
1515// Note: only dumps the section starting at start_idx and only up to
1516// dump_limit entries.
1517MaybeObject* LiveObjectList::Info(int start_idx, int dump_limit) {
danno@chromium.orgb6451162011-08-17 14:33:23 +00001518 Isolate* isolate = Isolate::Current();
1519 Factory* factory = isolate->factory();
1520
1521 HandleScope scope(isolate);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001522 MaybeObject* maybe_result;
1523
1524 int total_count = LiveObjectList::list_count();
1525 int dump_count = total_count;
1526
1527 // Adjust for where to start the dump.
1528 if (total_count == 0) {
1529 start_idx = 0; // Ensure this to get an empty list.
1530 } else if ((start_idx < 0) || (start_idx >= total_count)) {
1531 return Failure::Exception(); // invalid start.
1532 }
1533 dump_count -= start_idx;
1534
1535 // Adjust for the dump limit.
1536 if (dump_count > dump_limit) {
1537 dump_count = dump_limit;
1538 }
1539
1540 // Allocate an array to hold the result.
danno@chromium.orgb6451162011-08-17 14:33:23 +00001541 Handle<FixedArray> list = factory->NewFixedArray(dump_count);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001542 if (list->IsFailure()) return Object::cast(*list);
1543
1544 // Prefetch some needed symbols.
danno@chromium.orgb6451162011-08-17 14:33:23 +00001545 Handle<String> id_sym = factory->LookupAsciiSymbol("id");
1546 Handle<String> count_sym = factory->LookupAsciiSymbol("count");
1547 Handle<String> size_sym = factory->LookupAsciiSymbol("size");
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001548
1549 // Fill the array with the lol details.
1550 int idx = 0;
1551 LiveObjectList* lol = first_;
1552 while ((lol != NULL) && (idx < start_idx)) { // Skip tail entries.
1553 if (lol->id() != 0) {
1554 idx++;
1555 }
1556 lol = lol->next();
1557 }
1558 idx = 0;
1559 while ((lol != NULL) && (dump_limit != 0)) {
1560 if (lol->id() != 0) {
1561 int count;
1562 int size;
1563 count = lol->GetTotalObjCountAndSize(&size);
1564
danno@chromium.orgb6451162011-08-17 14:33:23 +00001565 Handle<JSObject> detail =
1566 factory->NewJSObject(isolate->object_function());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001567 if (detail->IsFailure()) return Object::cast(*detail);
1568
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001569 maybe_result = detail->SetProperty(*id_sym,
1570 Smi::FromInt(lol->id()),
1571 NONE,
1572 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001573 if (maybe_result->IsFailure()) return maybe_result;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001574 maybe_result = detail->SetProperty(*count_sym,
1575 Smi::FromInt(count),
1576 NONE,
1577 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001578 if (maybe_result->IsFailure()) return maybe_result;
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001579 maybe_result = detail->SetProperty(*size_sym,
1580 Smi::FromInt(size),
1581 NONE,
1582 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001583 if (maybe_result->IsFailure()) return maybe_result;
1584 list->set(idx++, *detail);
1585 dump_limit--;
1586 }
1587 lol = lol->next();
1588 }
1589
1590 // Return the result as a JS array.
danno@chromium.orgb6451162011-08-17 14:33:23 +00001591 Handle<JSObject> lols = factory->NewJSObject(isolate->array_function());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001592 Handle<JSArray>::cast(lols)->SetContent(*list);
1593
danno@chromium.orgb6451162011-08-17 14:33:23 +00001594 Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001595 if (result->IsFailure()) return Object::cast(*result);
1596
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001597 maybe_result = result->SetProperty(*count_sym,
1598 Smi::FromInt(total_count),
1599 NONE,
1600 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001601 if (maybe_result->IsFailure()) return maybe_result;
1602
danno@chromium.orgb6451162011-08-17 14:33:23 +00001603 Handle<String> first_sym = factory->LookupAsciiSymbol("first_index");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001604 maybe_result = result->SetProperty(*first_sym,
1605 Smi::FromInt(start_idx),
1606 NONE,
1607 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001608 if (maybe_result->IsFailure()) return maybe_result;
1609
danno@chromium.orgb6451162011-08-17 14:33:23 +00001610 Handle<String> lists_sym = factory->LookupAsciiSymbol("lists");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001611 maybe_result = result->SetProperty(*lists_sym,
1612 *lols,
1613 NONE,
1614 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001615 if (maybe_result->IsFailure()) return maybe_result;
1616
1617 return *result;
1618}
1619
1620
1621// Deletes all captured lols.
1622void LiveObjectList::Reset() {
1623 LiveObjectList *lol = last();
1624 // Just delete the last. Each lol will delete it's prev automatically.
1625 delete lol;
1626
1627 next_element_id_ = 1;
1628 list_count_ = 0;
1629 last_id_ = 0;
1630 first_ = NULL;
1631 last_ = NULL;
1632}
1633
1634
1635// Gets the object for the specified obj id.
1636Object* LiveObjectList::GetObj(int obj_id) {
1637 Element* element = FindElementFor<int>(GetElementId, obj_id);
1638 if (element != NULL) {
1639 return Object::cast(element->obj_);
1640 }
danno@chromium.orgb6451162011-08-17 14:33:23 +00001641 return HEAP->undefined_value();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001642}
1643
1644
1645// Gets the obj id for the specified address if valid.
1646int LiveObjectList::GetObjId(Object* obj) {
1647 // Make a heap object pointer from the address.
1648 HeapObject* hobj = HeapObject::cast(obj);
1649 Element* element = FindElementFor<HeapObject*>(GetElementObj, hobj);
1650 if (element != NULL) {
1651 return element->id_;
1652 }
1653 return 0; // Invalid address.
1654}
1655
1656
1657// Gets the obj id for the specified address if valid.
1658Object* LiveObjectList::GetObjId(Handle<String> address) {
1659 SmartPointer<char> addr_str =
1660 address->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
1661
danno@chromium.orgb6451162011-08-17 14:33:23 +00001662 Isolate* isolate = Isolate::Current();
1663
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001664 // Extract the address value from the string.
danno@chromium.orgb6451162011-08-17 14:33:23 +00001665 int value =
1666 static_cast<int>(StringToInt(isolate->unicode_cache(), *address, 16));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001667 Object* obj = reinterpret_cast<Object*>(value);
1668 return Smi::FromInt(GetObjId(obj));
1669}
1670
1671
1672// Helper class for copying HeapObjects.
1673class LolVisitor: public ObjectVisitor {
1674 public:
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001675 LolVisitor(HeapObject* target, Handle<HeapObject> handle_to_skip)
1676 : target_(target), handle_to_skip_(handle_to_skip), found_(false) {}
1677
1678 void VisitPointer(Object** p) { CheckPointer(p); }
1679
1680 void VisitPointers(Object** start, Object** end) {
1681 // Check all HeapObject pointers in [start, end).
1682 for (Object** p = start; !found() && p < end; p++) CheckPointer(p);
1683 }
1684
1685 inline bool found() const { return found_; }
1686 inline bool reset() { return found_ = false; }
1687
1688 private:
1689 inline void CheckPointer(Object** p) {
1690 Object* object = *p;
1691 if (HeapObject::cast(object) == target_) {
1692 // We may want to skip this handle because the handle may be a local
1693 // handle in a handle scope in one of our callers. Once we return,
1694 // that handle will be popped. Hence, we don't want to count it as
1695 // a root that would have kept the target object alive.
1696 if (!handle_to_skip_.is_null() &&
1697 handle_to_skip_.location() == reinterpret_cast<HeapObject**>(p)) {
1698 return; // Skip this handle.
1699 }
1700 found_ = true;
1701 }
1702 }
1703
1704 HeapObject* target_;
1705 Handle<HeapObject> handle_to_skip_;
1706 bool found_;
1707};
1708
1709
1710inline bool AddRootRetainerIfFound(const LolVisitor& visitor,
1711 LolFilter* filter,
1712 LiveObjectSummary *summary,
1713 void (*SetRootFound)(LiveObjectSummary *s),
1714 int start,
1715 int dump_limit,
1716 int* total_count,
1717 Handle<FixedArray> retainers_arr,
1718 int* count,
1719 int* index,
1720 const char* root_name,
1721 Handle<String> id_sym,
1722 Handle<String> desc_sym,
1723 Handle<String> size_sym,
1724 Handle<Object> error) {
1725 HandleScope scope;
1726
1727 // Scratch handles.
1728 Handle<JSObject> detail;
1729 Handle<String> desc;
1730 Handle<HeapObject> retainer;
1731
1732 if (visitor.found()) {
1733 if (!filter->is_active()) {
1734 (*total_count)++;
1735 if (summary) {
1736 SetRootFound(summary);
1737 } else if ((*total_count > start) && ((*index) < dump_limit)) {
1738 (*count)++;
1739 if (!retainers_arr.is_null()) {
1740 return AddObjDetail(retainers_arr,
1741 (*index)++,
1742 0,
1743 retainer,
1744 root_name,
1745 id_sym,
1746 desc_sym,
1747 size_sym,
1748 detail,
1749 desc,
1750 error);
1751 }
1752 }
1753 }
1754 }
1755 return true;
1756}
1757
1758
1759inline void SetFoundRoot(LiveObjectSummary *summary) {
1760 summary->set_found_root();
1761}
1762
1763
1764inline void SetFoundWeakRoot(LiveObjectSummary *summary) {
1765 summary->set_found_weak_root();
1766}
1767
1768
1769int LiveObjectList::GetRetainers(Handle<HeapObject> target,
1770 Handle<JSObject> instance_filter,
1771 Handle<FixedArray> retainers_arr,
1772 int start,
1773 int dump_limit,
1774 int* total_count,
1775 LolFilter* filter,
1776 LiveObjectSummary *summary,
1777 JSFunction* arguments_function,
1778 Handle<Object> error) {
1779 HandleScope scope;
1780
1781 // Scratch handles.
1782 Handle<JSObject> detail;
1783 Handle<String> desc;
1784 Handle<HeapObject> retainer;
1785
danno@chromium.orgb6451162011-08-17 14:33:23 +00001786 Isolate* isolate = Isolate::Current();
1787 Factory* factory = isolate->factory();
1788
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001789 // Prefetch some needed symbols.
danno@chromium.orgb6451162011-08-17 14:33:23 +00001790 Handle<String> id_sym = factory->LookupAsciiSymbol("id");
1791 Handle<String> desc_sym = factory->LookupAsciiSymbol("desc");
1792 Handle<String> size_sym = factory->LookupAsciiSymbol("size");
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001793
1794 NoHandleAllocation ha;
1795 int count = 0;
1796 int index = 0;
1797 Handle<JSObject> last_obj;
1798
1799 *total_count = 0;
1800
1801 // Iterate roots.
1802 LolVisitor lol_visitor(*target, target);
danno@chromium.orgb6451162011-08-17 14:33:23 +00001803 isolate->heap()->IterateStrongRoots(&lol_visitor, VISIT_ALL);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001804 if (!AddRootRetainerIfFound(lol_visitor,
1805 filter,
1806 summary,
1807 SetFoundRoot,
1808 start,
1809 dump_limit,
1810 total_count,
1811 retainers_arr,
1812 &count,
1813 &index,
1814 "<root>",
1815 id_sym,
1816 desc_sym,
1817 size_sym,
1818 error)) {
1819 return -1;
1820 }
1821
1822 lol_visitor.reset();
danno@chromium.orgb6451162011-08-17 14:33:23 +00001823 isolate->heap()->IterateWeakRoots(&lol_visitor, VISIT_ALL);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001824 if (!AddRootRetainerIfFound(lol_visitor,
1825 filter,
1826 summary,
1827 SetFoundWeakRoot,
1828 start,
1829 dump_limit,
1830 total_count,
1831 retainers_arr,
1832 &count,
1833 &index,
1834 "<weak root>",
1835 id_sym,
1836 desc_sym,
1837 size_sym,
1838 error)) {
1839 return -1;
1840 }
1841
1842 // Iterate the live object lists.
1843 LolIterator it(NULL, last());
1844 for (it.Init(); !it.Done() && (index < dump_limit); it.Next()) {
1845 HeapObject* heap_obj = it.Obj();
1846
1847 // Only look at all JSObjects.
1848 if (heap_obj->IsJSObject()) {
1849 // Skip context extension objects and argument arrays as these are
1850 // checked in the context of functions using them.
1851 JSObject* obj = JSObject::cast(heap_obj);
1852 if (obj->IsJSContextExtensionObject() ||
1853 obj->map()->constructor() == arguments_function) {
1854 continue;
1855 }
1856
1857 // Check if the JS object has a reference to the object looked for.
1858 if (obj->ReferencesObject(*target)) {
1859 // Check instance filter if supplied. This is normally used to avoid
1860 // references from mirror objects (see Runtime_IsInPrototypeChain).
1861 if (!instance_filter->IsUndefined()) {
1862 Object* V = obj;
1863 while (true) {
1864 Object* prototype = V->GetPrototype();
1865 if (prototype->IsNull()) {
1866 break;
1867 }
1868 if (*instance_filter == prototype) {
1869 obj = NULL; // Don't add this object.
1870 break;
1871 }
1872 V = prototype;
1873 }
1874 }
1875
1876 if (obj != NULL) {
1877 // Skip objects that have been filtered out.
1878 if (filter->Matches(heap_obj)) {
1879 continue;
1880 }
1881
1882 // Valid reference found add to instance array if supplied an update
1883 // count.
1884 last_obj = Handle<JSObject>(obj);
1885 (*total_count)++;
1886
1887 if (summary != NULL) {
1888 summary->Add(heap_obj);
1889 } else if ((*total_count > start) && (index < dump_limit)) {
1890 count++;
1891 if (!retainers_arr.is_null()) {
1892 retainer = Handle<HeapObject>(heap_obj);
1893 bool success = AddObjDetail(retainers_arr,
1894 index++,
1895 it.Id(),
1896 retainer,
1897 NULL,
1898 id_sym,
1899 desc_sym,
1900 size_sym,
1901 detail,
1902 desc,
1903 error);
1904 if (!success) return -1;
1905 }
1906 }
1907 }
1908 }
1909 }
1910 }
1911
1912 // Check for circular reference only. This can happen when the object is only
1913 // referenced from mirrors and has a circular reference in which case the
1914 // object is not really alive and would have been garbage collected if not
1915 // referenced from the mirror.
1916
1917 if (*total_count == 1 && !last_obj.is_null() && *last_obj == *target) {
1918 count = 0;
1919 *total_count = 0;
1920 }
1921
1922 return count;
1923}
1924
1925
1926MaybeObject* LiveObjectList::GetObjRetainers(int obj_id,
1927 Handle<JSObject> instance_filter,
1928 bool verbose,
1929 int start,
1930 int dump_limit,
1931 Handle<JSObject> filter_obj) {
danno@chromium.orgb6451162011-08-17 14:33:23 +00001932 Isolate* isolate = Isolate::Current();
1933 Factory* factory = isolate->factory();
1934 Heap* heap = isolate->heap();
1935
1936 HandleScope scope(isolate);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001937
1938 // Get the target object.
1939 HeapObject* heap_obj = HeapObject::cast(GetObj(obj_id));
danno@chromium.orgb6451162011-08-17 14:33:23 +00001940 if (heap_obj == heap->undefined_value()) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001941 return heap_obj;
1942 }
1943
1944 Handle<HeapObject> target = Handle<HeapObject>(heap_obj);
1945
1946 // Get the constructor function for context extension and arguments array.
1947 JSObject* arguments_boilerplate =
danno@chromium.orgb6451162011-08-17 14:33:23 +00001948 isolate->context()->global_context()->arguments_boilerplate();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001949 JSFunction* arguments_function =
1950 JSFunction::cast(arguments_boilerplate->map()->constructor());
1951
1952 Handle<JSFunction> args_function = Handle<JSFunction>(arguments_function);
1953 LolFilter filter(filter_obj);
1954
1955 if (!verbose) {
1956 RetainersSummaryWriter writer(target, instance_filter, args_function);
1957 return SummarizePrivate(&writer, &filter, true);
1958
1959 } else {
1960 RetainersDumpWriter writer(target, instance_filter, args_function);
1961 Object* body_obj;
1962 MaybeObject* maybe_result =
1963 DumpPrivate(&writer, start, dump_limit, &filter);
1964 if (!maybe_result->ToObject(&body_obj)) {
1965 return maybe_result;
1966 }
1967
1968 // Set body.id.
1969 Handle<JSObject> body = Handle<JSObject>(JSObject::cast(body_obj));
danno@chromium.orgb6451162011-08-17 14:33:23 +00001970 Handle<String> id_sym = factory->LookupAsciiSymbol("id");
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001971 maybe_result = body->SetProperty(*id_sym,
1972 Smi::FromInt(obj_id),
1973 NONE,
1974 kNonStrictMode);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001975 if (maybe_result->IsFailure()) return maybe_result;
1976
1977 return *body;
1978 }
1979}
1980
1981
1982Object* LiveObjectList::PrintObj(int obj_id) {
1983 Object* obj = GetObj(obj_id);
1984 if (!obj) {
danno@chromium.orgb6451162011-08-17 14:33:23 +00001985 return HEAP->undefined_value();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001986 }
1987
1988 EmbeddedVector<char, 128> temp_filename;
1989 static int temp_count = 0;
1990 const char* path_prefix = ".";
1991
danno@chromium.orgb6451162011-08-17 14:33:23 +00001992 Isolate* isolate = Isolate::Current();
1993 Factory* factory = isolate->factory();
1994 Heap* heap = isolate->heap();
1995
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001996 if (FLAG_lol_workdir) {
1997 path_prefix = FLAG_lol_workdir;
1998 }
1999 OS::SNPrintF(temp_filename, "%s/lol-print-%d", path_prefix, ++temp_count);
2000
2001 FILE* f = OS::FOpen(temp_filename.start(), "w+");
2002
2003 PrintF(f, "@%d ", LiveObjectList::GetObjId(obj));
2004#ifdef OBJECT_PRINT
2005#ifdef INSPECTOR
2006 Inspector::DumpObjectType(f, obj);
2007#endif // INSPECTOR
2008 PrintF(f, "\n");
2009 obj->Print(f);
2010#else // !OBJECT_PRINT
2011 obj->ShortPrint(f);
2012#endif // !OBJECT_PRINT
2013 PrintF(f, "\n");
2014 Flush(f);
2015 fclose(f);
2016
2017 // Create a string from the temp_file.
2018 // Note: the mmapped resource will take care of closing the file.
2019 MemoryMappedExternalResource* resource =
2020 new MemoryMappedExternalResource(temp_filename.start(), true);
2021 if (resource->exists() && !resource->is_empty()) {
2022 ASSERT(resource->IsAscii());
2023 Handle<String> dump_string =
danno@chromium.orgb6451162011-08-17 14:33:23 +00002024 factory->NewExternalStringFromAscii(resource);
2025 heap->external_string_table()->AddString(*dump_string);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002026 return *dump_string;
2027 } else {
2028 delete resource;
2029 }
danno@chromium.orgb6451162011-08-17 14:33:23 +00002030 return HEAP->undefined_value();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002031}
2032
2033
2034class LolPathTracer: public PathTracer {
2035 public:
2036 LolPathTracer(FILE* out,
2037 Object* search_target,
2038 WhatToFind what_to_find)
2039 : PathTracer(search_target, what_to_find, VISIT_ONLY_STRONG), out_(out) {}
2040
2041 private:
2042 void ProcessResults();
2043
2044 FILE* out_;
2045};
2046
2047
2048void LolPathTracer::ProcessResults() {
2049 if (found_target_) {
2050 PrintF(out_, "=====================================\n");
2051 PrintF(out_, "==== Path to object ====\n");
2052 PrintF(out_, "=====================================\n\n");
2053
2054 ASSERT(!object_stack_.is_empty());
2055 Object* prev = NULL;
2056 for (int i = 0, index = 0; i < object_stack_.length(); i++) {
2057 Object* obj = object_stack_[i];
2058
2059 // Skip this object if it is basically the internals of the
2060 // previous object (which would have dumped its details already).
2061 if (prev && prev->IsJSObject() &&
2062 (obj != search_target_)) {
2063 JSObject* jsobj = JSObject::cast(prev);
2064 if (obj->IsFixedArray() &&
2065 jsobj->properties() == FixedArray::cast(obj)) {
2066 // Skip this one because it would have been printed as the
2067 // properties of the last object already.
2068 continue;
2069 } else if (obj->IsHeapObject() &&
2070 jsobj->elements() == HeapObject::cast(obj)) {
2071 // Skip this one because it would have been printed as the
2072 // elements of the last object already.
2073 continue;
2074 }
2075 }
2076
2077 // Print a connecting arrow.
2078 if (i > 0) PrintF(out_, "\n |\n |\n V\n\n");
2079
2080 // Print the object index.
2081 PrintF(out_, "[%d] ", ++index);
2082
2083 // Print the LOL object ID:
2084 int id = LiveObjectList::GetObjId(obj);
2085 if (id > 0) PrintF(out_, "@%d ", id);
2086
2087#ifdef OBJECT_PRINT
2088#ifdef INSPECTOR
2089 Inspector::DumpObjectType(out_, obj);
2090#endif // INSPECTOR
2091 PrintF(out_, "\n");
2092 obj->Print(out_);
2093#else // !OBJECT_PRINT
2094 obj->ShortPrint(out_);
2095 PrintF(out_, "\n");
2096#endif // !OBJECT_PRINT
2097 Flush(out_);
2098 }
2099 PrintF(out_, "\n");
2100 PrintF(out_, "=====================================\n\n");
2101 Flush(out_);
2102 }
2103}
2104
2105
2106Object* LiveObjectList::GetPathPrivate(HeapObject* obj1, HeapObject* obj2) {
2107 EmbeddedVector<char, 128> temp_filename;
2108 static int temp_count = 0;
2109 const char* path_prefix = ".";
2110
2111 if (FLAG_lol_workdir) {
2112 path_prefix = FLAG_lol_workdir;
2113 }
2114 OS::SNPrintF(temp_filename, "%s/lol-getpath-%d", path_prefix, ++temp_count);
2115
2116 FILE* f = OS::FOpen(temp_filename.start(), "w+");
2117
danno@chromium.orgb6451162011-08-17 14:33:23 +00002118 Isolate* isolate = Isolate::Current();
2119 Factory* factory = isolate->factory();
2120 Heap* heap = isolate->heap();
2121
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002122 // Save the previous verbosity.
2123 bool prev_verbosity = FLAG_use_verbose_printer;
2124 FLAG_use_verbose_printer = false;
2125
2126 // Dump the paths.
2127 {
2128 // The tracer needs to be scoped because its usage asserts no allocation,
2129 // and we need to allocate the result string below.
2130 LolPathTracer tracer(f, obj2, LolPathTracer::FIND_FIRST);
2131
2132 bool found = false;
2133 if (obj1 == NULL) {
2134 // Check for ObjectGroups that references this object.
2135 // TODO(mlam): refactor this to be more modular.
2136 {
danno@chromium.orgb6451162011-08-17 14:33:23 +00002137 List<ObjectGroup*>* groups = isolate->global_handles()->object_groups();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002138 for (int i = 0; i < groups->length(); i++) {
2139 ObjectGroup* group = groups->at(i);
2140 if (group == NULL) continue;
2141
2142 bool found_group = false;
danno@chromium.orgb6451162011-08-17 14:33:23 +00002143 for (size_t j = 0; j < group->length_; j++) {
2144 Object* object = *(group->objects_[j]);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002145 HeapObject* hobj = HeapObject::cast(object);
2146 if (obj2 == hobj) {
2147 found_group = true;
2148 break;
2149 }
2150 }
2151
2152 if (found_group) {
2153 PrintF(f,
2154 "obj %p is a member of object group %p {\n",
2155 reinterpret_cast<void*>(obj2),
2156 reinterpret_cast<void*>(group));
danno@chromium.orgb6451162011-08-17 14:33:23 +00002157 for (size_t j = 0; j < group->length_; j++) {
2158 Object* object = *(group->objects_[j]);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002159 if (!object->IsHeapObject()) continue;
2160
2161 HeapObject* hobj = HeapObject::cast(object);
2162 int id = GetObjId(hobj);
2163 if (id != 0) {
2164 PrintF(f, " @%d:", id);
2165 } else {
2166 PrintF(f, " <no id>:");
2167 }
2168
2169 char buffer[512];
2170 GenerateObjectDesc(hobj, buffer, sizeof(buffer));
2171 PrintF(f, " %s", buffer);
2172 if (hobj == obj2) {
2173 PrintF(f, " <===");
2174 }
2175 PrintF(f, "\n");
2176 }
2177 PrintF(f, "}\n");
2178 }
2179 }
2180 }
2181
2182 PrintF(f, "path from roots to obj %p\n", reinterpret_cast<void*>(obj2));
danno@chromium.orgb6451162011-08-17 14:33:23 +00002183 heap->IterateRoots(&tracer, VISIT_ONLY_STRONG);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002184 found = tracer.found();
2185
2186 if (!found) {
2187 PrintF(f, " No paths found. Checking symbol tables ...\n");
danno@chromium.orgb6451162011-08-17 14:33:23 +00002188 SymbolTable* symbol_table = HEAP->raw_unchecked_symbol_table();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002189 tracer.VisitPointers(reinterpret_cast<Object**>(&symbol_table),
2190 reinterpret_cast<Object**>(&symbol_table)+1);
2191 found = tracer.found();
2192 if (!found) {
2193 symbol_table->IteratePrefix(&tracer);
2194 found = tracer.found();
2195 }
2196 }
2197
2198 if (!found) {
2199 PrintF(f, " No paths found. Checking weak roots ...\n");
2200 // Check weak refs next.
danno@chromium.orgb6451162011-08-17 14:33:23 +00002201 isolate->global_handles()->IterateWeakRoots(&tracer);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002202 found = tracer.found();
2203 }
2204
2205 } else {
2206 PrintF(f, "path from obj %p to obj %p:\n",
2207 reinterpret_cast<void*>(obj1), reinterpret_cast<void*>(obj2));
2208 tracer.TracePathFrom(reinterpret_cast<Object**>(&obj1));
2209 found = tracer.found();
2210 }
2211
2212 if (!found) {
2213 PrintF(f, " No paths found\n\n");
2214 }
2215 }
2216
2217 // Flush and clean up the dumped file.
2218 Flush(f);
2219 fclose(f);
2220
2221 // Restore the previous verbosity.
2222 FLAG_use_verbose_printer = prev_verbosity;
2223
2224 // Create a string from the temp_file.
2225 // Note: the mmapped resource will take care of closing the file.
2226 MemoryMappedExternalResource* resource =
2227 new MemoryMappedExternalResource(temp_filename.start(), true);
2228 if (resource->exists() && !resource->is_empty()) {
2229 ASSERT(resource->IsAscii());
2230 Handle<String> path_string =
danno@chromium.orgb6451162011-08-17 14:33:23 +00002231 factory->NewExternalStringFromAscii(resource);
2232 heap->external_string_table()->AddString(*path_string);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002233 return *path_string;
2234 } else {
2235 delete resource;
2236 }
danno@chromium.orgb6451162011-08-17 14:33:23 +00002237 return heap->undefined_value();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002238}
2239
2240
2241Object* LiveObjectList::GetPath(int obj_id1,
2242 int obj_id2,
2243 Handle<JSObject> instance_filter) {
2244 HandleScope scope;
2245
2246 // Get the target object.
2247 HeapObject* obj1 = NULL;
2248 if (obj_id1 != 0) {
2249 obj1 = HeapObject::cast(GetObj(obj_id1));
danno@chromium.orgb6451162011-08-17 14:33:23 +00002250 if (obj1 == HEAP->undefined_value()) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002251 return obj1;
2252 }
2253 }
2254
2255 HeapObject* obj2 = HeapObject::cast(GetObj(obj_id2));
danno@chromium.orgb6451162011-08-17 14:33:23 +00002256 if (obj2 == HEAP->undefined_value()) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002257 return obj2;
2258 }
2259
2260 return GetPathPrivate(obj1, obj2);
2261}
2262
2263
2264void LiveObjectList::DoProcessNonLive(HeapObject *obj) {
2265 // We should only be called if we have at least one lol to search.
2266 ASSERT(last() != NULL);
2267 Element* element = last()->Find(obj);
2268 if (element != NULL) {
2269 NullifyNonLivePointer(&element->obj_);
2270 }
2271}
2272
2273
2274void LiveObjectList::IterateElementsPrivate(ObjectVisitor* v) {
2275 LiveObjectList* lol = last();
2276 while (lol != NULL) {
2277 Element* elements = lol->elements_;
2278 int count = lol->obj_count_;
2279 for (int i = 0; i < count; i++) {
2280 HeapObject** p = &elements[i].obj_;
2281 v->VisitPointer(reinterpret_cast<Object **>(p));
2282 }
2283 lol = lol->prev_;
2284 }
2285}
2286
2287
2288// Purpose: Called by GCEpilogue to purge duplicates. Not to be called by
2289// anyone else.
2290void LiveObjectList::PurgeDuplicates() {
2291 bool is_sorted = false;
2292 LiveObjectList* lol = last();
2293 if (!lol) {
2294 return; // Nothing to purge.
2295 }
2296
2297 int total_count = lol->TotalObjCount();
2298 if (!total_count) {
2299 return; // Nothing to purge.
2300 }
2301
2302 Element* elements = NewArray<Element>(total_count);
2303 int count = 0;
2304
2305 // Copy all the object elements into a consecutive array.
2306 while (lol) {
2307 memcpy(&elements[count], lol->elements_, lol->obj_count_ * sizeof(Element));
2308 count += lol->obj_count_;
2309 lol = lol->prev_;
2310 }
2311 qsort(elements, total_count, sizeof(Element),
2312 reinterpret_cast<RawComparer>(CompareElement));
2313
2314 ASSERT(count == total_count);
2315
2316 // Iterate over all objects in the consolidated list and check for dups.
2317 total_count--;
2318 for (int i = 0; i < total_count; ) {
2319 Element* curr = &elements[i];
2320 HeapObject* curr_obj = curr->obj_;
2321 int j = i+1;
2322 bool done = false;
2323
2324 while (!done && (j < total_count)) {
2325 // Process if the element's object is still live after the current GC.
2326 // Non-live objects will be converted to SMIs i.e. not HeapObjects.
2327 if (curr_obj->IsHeapObject()) {
2328 Element* next = &elements[j];
2329 HeapObject* next_obj = next->obj_;
2330 if (next_obj->IsHeapObject()) {
2331 if (curr_obj != next_obj) {
2332 done = true;
2333 continue; // Live object but no match. Move on.
2334 }
2335
2336 // NOTE: we've just GCed the LOLs. Hence, they are no longer sorted.
2337 // Since we detected at least one need to search for entries, we'll
2338 // sort it to enable the use of NullifyMostRecent() below. We only
2339 // need to sort it once (except for one exception ... see below).
2340 if (!is_sorted) {
2341 SortAll();
2342 is_sorted = true;
2343 }
2344
2345 // We have a match. Need to nullify the most recent ref to this
2346 // object. We'll keep the oldest ref:
2347 // Note: we will nullify the element record in the LOL
2348 // database, not in the local sorted copy of the elements.
2349 NullifyMostRecent(curr_obj);
2350 }
2351 }
2352 // Either the object was already marked for purging, or we just marked
2353 // it. Either way, if there's more than one dup, then we need to check
2354 // the next element for another possible dup against the current as well
2355 // before we move on. So, here we go.
2356 j++;
2357 }
2358
2359 // We can move on to checking the match on the next element.
2360 i = j;
2361 }
2362
2363 DeleteArray<Element>(elements);
2364}
2365
2366
2367// Purpose: Purges dead objects and resorts the LOLs.
2368void LiveObjectList::GCEpiloguePrivate() {
2369 // Note: During the GC, ConsStrings may be collected and pointers may be
2370 // forwarded to its constituent string. As a result, we may find dupes of
2371 // objects references in the LOL list.
2372 // Another common way we get dups is that free chunks that have been swept
2373 // in the oldGen heap may be kept as ByteArray objects in a free list.
2374 //
2375 // When we promote live objects from the youngGen, the object may be moved
2376 // to the start of these free chunks. Since there is no free or move event
2377 // for the free chunks, their addresses will show up 2 times: once for their
2378 // original free ByteArray selves, and once for the newly promoted youngGen
2379 // object. Hence, we can get a duplicate address in the LOL again.
2380 //
2381 // We need to eliminate these dups because the LOL implementation expects to
2382 // only have at most one unique LOL reference to any object at any time.
2383 PurgeDuplicates();
2384
2385 // After the GC, sweep away all free'd Elements and compact.
2386 LiveObjectList *prev = NULL;
2387 LiveObjectList *next = NULL;
2388
2389 // Iterating from the youngest lol to the oldest lol.
2390 for (LiveObjectList *lol = last(); lol; lol = prev) {
2391 Element* elements = lol->elements_;
2392 prev = lol->prev(); // Save the prev.
2393
2394 // Remove any references to collected objects.
2395 int i = 0;
2396 while (i < lol->obj_count_) {
2397 Element& element = elements[i];
2398 if (!element.obj_->IsHeapObject()) {
2399 // If the HeapObject address was converted into a SMI, then this
2400 // is a dead object. Copy the last element over this one.
2401 element = elements[lol->obj_count_ - 1];
2402 lol->obj_count_--;
2403 // We've just moved the last element into this index. We'll revisit
2404 // this index again. Hence, no need to increment the iterator.
2405 } else {
2406 i++; // Look at the next element next.
2407 }
2408 }
2409
2410 int new_count = lol->obj_count_;
2411
2412 // Check if there are any more elements to keep after purging the dead ones.
2413 if (new_count == 0) {
2414 DeleteArray<Element>(elements);
2415 lol->elements_ = NULL;
2416 lol->capacity_ = 0;
2417 ASSERT(lol->obj_count_ == 0);
2418
2419 // If the list is also invisible, the clean up the list as well.
2420 if (lol->id_ == 0) {
2421 // Point the next lol's prev to this lol's prev.
2422 if (next) {
2423 next->prev_ = lol->prev_;
2424 } else {
2425 last_ = lol->prev_;
2426 }
2427
2428 // Delete this now empty and invisible lol.
2429 delete lol;
2430
2431 // Don't point the next to this lol since it is now deleted.
2432 // Leave the next pointer pointing to the current lol.
2433 continue;
2434 }
2435
2436 } else {
2437 // If the obj_count_ is less than the capacity and the difference is
2438 // greater than a specified threshold, then we should shrink the list.
2439 int diff = lol->capacity_ - new_count;
2440 const int kMaxUnusedSpace = 64;
2441 if (diff > kMaxUnusedSpace) { // Threshold for shrinking.
2442 // Shrink the list.
2443 Element *new_elements = NewArray<Element>(new_count);
2444 memcpy(new_elements, elements, new_count * sizeof(Element));
2445
2446 DeleteArray<Element>(elements);
2447 lol->elements_ = new_elements;
2448 lol->capacity_ = new_count;
2449 }
2450 ASSERT(lol->obj_count_ == new_count);
2451
2452 lol->Sort(); // We've moved objects. Re-sort in case.
2453 }
2454
2455 // Save the next (for the previous link) in case we need it later.
2456 next = lol;
2457 }
2458
2459#ifdef VERIFY_LOL
2460 if (FLAG_verify_lol) {
2461 Verify();
2462 }
2463#endif
2464}
2465
2466
2467#ifdef VERIFY_LOL
2468void LiveObjectList::Verify(bool match_heap_exactly) {
2469 OS::Print("Verifying the LiveObjectList database:\n");
2470
2471 LiveObjectList* lol = last();
2472 if (lol == NULL) {
2473 OS::Print(" No lol database to verify\n");
2474 return;
2475 }
2476
2477 OS::Print(" Preparing the lol database ...\n");
2478 int total_count = lol->TotalObjCount();
2479
2480 Element* elements = NewArray<Element>(total_count);
2481 int count = 0;
2482
2483 // Copy all the object elements into a consecutive array.
2484 OS::Print(" Copying the lol database ...\n");
2485 while (lol != NULL) {
2486 memcpy(&elements[count], lol->elements_, lol->obj_count_ * sizeof(Element));
2487 count += lol->obj_count_;
2488 lol = lol->prev_;
2489 }
2490 qsort(elements, total_count, sizeof(Element),
2491 reinterpret_cast<RawComparer>(CompareElement));
2492
2493 ASSERT(count == total_count);
2494
2495 // Iterate over all objects in the heap and check for:
2496 // 1. object in LOL but not in heap i.e. error.
2497 // 2. object in heap but not in LOL (possibly not an error). Usually
2498 // just means that we don't have the a capture of the latest heap.
2499 // That is unless we did this verify immediately after a capture,
2500 // and specified match_heap_exactly = true.
2501
2502 int number_of_heap_objects = 0;
2503 int number_of_matches = 0;
2504 int number_not_in_heap = total_count;
2505 int number_not_in_lol = 0;
2506
2507 OS::Print(" Start verify ...\n");
2508 OS::Print(" Verifying ...");
2509 Flush();
2510 HeapIterator iterator(HeapIterator::kFilterFreeListNodes);
2511 HeapObject* heap_obj = NULL;
2512 while ((heap_obj = iterator.next()) != NULL) {
2513 number_of_heap_objects++;
2514
2515 // Check if the heap_obj is in the lol.
2516 Element key;
2517 key.obj_ = heap_obj;
2518
2519 Element* result = reinterpret_cast<Element*>(
2520 bsearch(&key, elements, total_count, sizeof(Element),
2521 reinterpret_cast<RawComparer>(CompareElement)));
2522
2523 if (result != NULL) {
2524 number_of_matches++;
2525 number_not_in_heap--;
2526 // Mark it as found by changing it into a SMI (mask off low bit).
2527 // Note: we cannot use HeapObject::cast() here because it asserts that
2528 // the HeapObject bit is set on the address, but we're unsetting it on
2529 // purpose here for our marking.
2530 result->obj_ = reinterpret_cast<HeapObject*>(heap_obj->address());
2531
2532 } else {
2533 number_not_in_lol++;
2534 if (match_heap_exactly) {
2535 OS::Print("heap object %p NOT in lol database\n", heap_obj);
2536 }
2537 }
2538 // Show some sign of life.
2539 if (number_of_heap_objects % 1000 == 0) {
2540 OS::Print(".");
2541 fflush(stdout);
2542 }
2543 }
2544 OS::Print("\n");
2545
2546 // Reporting lol objects not found in the heap.
2547 if (number_not_in_heap) {
2548 int found = 0;
2549 for (int i = 0; (i < total_count) && (found < number_not_in_heap); i++) {
2550 Element& element = elements[i];
2551 if (element.obj_->IsHeapObject()) {
2552 OS::Print("lol database object [%d of %d] %p NOT in heap\n",
2553 i, total_count, element.obj_);
2554 found++;
2555 }
2556 }
2557 }
2558
2559 DeleteArray<Element>(elements);
2560
2561 OS::Print("number of objects in lol database %d\n", total_count);
2562 OS::Print("number of heap objects .......... %d\n", number_of_heap_objects);
2563 OS::Print("number of matches ............... %d\n", number_of_matches);
2564 OS::Print("number NOT in heap .............. %d\n", number_not_in_heap);
2565 OS::Print("number NOT in lol database ...... %d\n", number_not_in_lol);
2566
2567 if (number_of_matches != total_count) {
2568 OS::Print(" *** ERROR: "
2569 "NOT all lol database objects match heap objects.\n");
2570 }
2571 if (number_not_in_heap != 0) {
2572 OS::Print(" *** ERROR: %d lol database objects not found in heap.\n",
2573 number_not_in_heap);
2574 }
2575 if (match_heap_exactly) {
2576 if (!(number_not_in_lol == 0)) {
2577 OS::Print(" *** ERROR: %d heap objects NOT found in lol database.\n",
2578 number_not_in_lol);
2579 }
2580 }
2581
2582 ASSERT(number_of_matches == total_count);
2583 ASSERT(number_not_in_heap == 0);
2584 ASSERT(number_not_in_lol == (number_of_heap_objects - total_count));
2585 if (match_heap_exactly) {
2586 ASSERT(total_count == number_of_heap_objects);
2587 ASSERT(number_not_in_lol == 0);
2588 }
2589
2590 OS::Print(" Verify the lol database is sorted ...\n");
2591 lol = last();
2592 while (lol != NULL) {
2593 Element* elements = lol->elements_;
2594 for (int i = 0; i < lol->obj_count_ - 1; i++) {
2595 if (elements[i].obj_ >= elements[i+1].obj_) {
2596 OS::Print(" *** ERROR: lol %p obj[%d] %p > obj[%d] %p\n",
2597 lol, i, elements[i].obj_, i+1, elements[i+1].obj_);
2598 }
2599 }
2600 lol = lol->prev_;
2601 }
2602
2603 OS::Print(" DONE verifying.\n\n\n");
2604}
2605
2606
2607void LiveObjectList::VerifyNotInFromSpace() {
2608 OS::Print("VerifyNotInFromSpace() ...\n");
2609 LolIterator it(NULL, last());
danno@chromium.orgb6451162011-08-17 14:33:23 +00002610 Heap* heap = ISOLATE->heap();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002611 int i = 0;
2612 for (it.Init(); !it.Done(); it.Next()) {
2613 HeapObject* heap_obj = it.Obj();
danno@chromium.orgb6451162011-08-17 14:33:23 +00002614 if (heap->InFromSpace(heap_obj)) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002615 OS::Print(" ERROR: VerifyNotInFromSpace: [%d] obj %p in From space %p\n",
danno@chromium.orgb6451162011-08-17 14:33:23 +00002616 i++, heap_obj, heap->new_space()->FromSpaceLow());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002617 }
2618 }
2619}
2620#endif // VERIFY_LOL
2621
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00002622
2623} } // namespace v8::internal
2624
2625#endif // LIVE_OBJECT_LIST