OLD | NEW |
| (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 | |
OLD | NEW |