1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/v8.h"
6
7 #include "src/heap-snapshot-generator-inl.h"
8
9 #include "src/allocation-tracker.h"
10 #include "src/code-stubs.h"
11 #include "src/conversions.h"
12 #include "src/debug.h"
13 #include "src/heap-profiler.h"
14 #include "src/types.h"
15
16 namespace v8 {
17 namespace internal {
18
19
HeapGraphEdge(Type type,const char * name,int from,int to)20 HeapGraphEdge::HeapGraphEdge(Type type, const char* name, int from, int to)
21 : type_(type),
22 from_index_(from),
23 to_index_(to),
24 name_(name) {
25 ASSERT(type == kContextVariable
26 || type == kProperty
27 || type == kInternal
28 || type == kShortcut
29 || type == kWeak);
30 }
31
32
HeapGraphEdge(Type type,int index,int from,int to)33 HeapGraphEdge::HeapGraphEdge(Type type, int index, int from, int to)
34 : type_(type),
35 from_index_(from),
36 to_index_(to),
37 index_(index) {
38 ASSERT(type == kElement || type == kHidden);
39 }
40
41
ReplaceToIndexWithEntry(HeapSnapshot * snapshot)42 void HeapGraphEdge::ReplaceToIndexWithEntry(HeapSnapshot* snapshot) {
43 to_entry_ = &snapshot->entries()[to_index_];
44 }
45
46
47 const int HeapEntry::kNoEntry = -1;
48
HeapEntry(HeapSnapshot * snapshot,Type type,const char * name,SnapshotObjectId id,size_t self_size,unsigned trace_node_id)49 HeapEntry::HeapEntry(HeapSnapshot* snapshot,
50 Type type,
51 const char* name,
52 SnapshotObjectId id,
53 size_t self_size,
54 unsigned trace_node_id)
55 : type_(type),
56 children_count_(0),
57 children_index_(-1),
58 self_size_(self_size),
59 snapshot_(snapshot),
60 name_(name),
61 id_(id),
62 trace_node_id_(trace_node_id) { }
63
64
SetNamedReference(HeapGraphEdge::Type type,const char * name,HeapEntry * entry)65 void HeapEntry::SetNamedReference(HeapGraphEdge::Type type,
66 const char* name,
67 HeapEntry* entry) {
68 HeapGraphEdge edge(type, name, this->index(), entry->index());
69 snapshot_->edges().Add(edge);
70 ++children_count_;
71 }
72
73
SetIndexedReference(HeapGraphEdge::Type type,int index,HeapEntry * entry)74 void HeapEntry::SetIndexedReference(HeapGraphEdge::Type type,
75 int index,
76 HeapEntry* entry) {
77 HeapGraphEdge edge(type, index, this->index(), entry->index());
78 snapshot_->edges().Add(edge);
79 ++children_count_;
80 }
81
82
Print(const char * prefix,const char * edge_name,int max_depth,int indent)83 void HeapEntry::Print(
84 const char* prefix, const char* edge_name, int max_depth, int indent) {
85 STATIC_ASSERT(sizeof(unsigned) == sizeof(id()));
86 OS::Print("%6" V8PRIuPTR " @%6u %*c %s%s: ",
87 self_size(), id(), indent, ' ', prefix, edge_name);
88 if (type() != kString) {
89 OS::Print("%s %.40s\n", TypeAsString(), name_);
90 } else {
91 OS::Print("\"");
92 const char* c = name_;
93 while (*c && (c - name_) <= 40) {
94 if (*c != '\n')
95 OS::Print("%c", *c);
96 else
97 OS::Print("\\n");
98 ++c;
99 }
100 OS::Print("\"\n");
101 }
102 if (--max_depth == 0) return;
103 Vector<HeapGraphEdge*> ch = children();
104 for (int i = 0; i < ch.length(); ++i) {
105 HeapGraphEdge& edge = *ch[i];
106 const char* edge_prefix = "";
107 EmbeddedVector<char, 64> index;
108 const char* edge_name = index.start();
109 switch (edge.type()) {
110 case HeapGraphEdge::kContextVariable:
111 edge_prefix = "#";
112 edge_name = edge.name();
113 break;
114 case HeapGraphEdge::kElement:
115 SNPrintF(index, "%d", edge.index());
116 break;
117 case HeapGraphEdge::kInternal:
118 edge_prefix = "$";
119 edge_name = edge.name();
120 break;
121 case HeapGraphEdge::kProperty:
122 edge_name = edge.name();
123 break;
124 case HeapGraphEdge::kHidden:
125 edge_prefix = "$";
126 SNPrintF(index, "%d", edge.index());
127 break;
128 case HeapGraphEdge::kShortcut:
129 edge_prefix = "^";
130 edge_name = edge.name();
131 break;
132 case HeapGraphEdge::kWeak:
133 edge_prefix = "w";
134 edge_name = edge.name();
135 break;
136 default:
137 SNPrintF(index, "!!! unknown edge type: %d ", edge.type());
138 }
139 edge.to()->Print(edge_prefix, edge_name, max_depth, indent + 2);
140 }
141 }
142
143
TypeAsString()144 const char* HeapEntry::TypeAsString() {
145 switch (type()) {
146 case kHidden: return "/hidden/";
147 case kObject: return "/object/";
148 case kClosure: return "/closure/";
149 case kString: return "/string/";
150 case kCode: return "/code/";
151 case kArray: return "/array/";
152 case kRegExp: return "/regexp/";
153 case kHeapNumber: return "/number/";
154 case kNative: return "/native/";
155 case kSynthetic: return "/synthetic/";
156 case kConsString: return "/concatenated string/";
157 case kSlicedString: return "/sliced string/";
158 case kSymbol: return "/symbol/";
159 default: return "???";
160 }
161 }
162
163
164 // It is very important to keep objects that form a heap snapshot
165 // as small as possible.
166 namespace { // Avoid littering the global namespace.
167
168 template <size_t ptr_size> struct SnapshotSizeConstants;
169
170 template <> struct SnapshotSizeConstants<4> {
171 static const int kExpectedHeapGraphEdgeSize = 12;
172 static const int kExpectedHeapEntrySize = 28;
173 };
174
175 template <> struct SnapshotSizeConstants<8> {
176 static const int kExpectedHeapGraphEdgeSize = 24;
177 static const int kExpectedHeapEntrySize = 40;
178 };
179
180 } // namespace
181
182
HeapSnapshot(HeapProfiler * profiler,const char * title,unsigned uid)183 HeapSnapshot::HeapSnapshot(HeapProfiler* profiler,
184 const char* title,
185 unsigned uid)
186 : profiler_(profiler),
187 title_(title),
188 uid_(uid),
189 root_index_(HeapEntry::kNoEntry),
190 gc_roots_index_(HeapEntry::kNoEntry),
191 natives_root_index_(HeapEntry::kNoEntry),
192 max_snapshot_js_object_id_(0) {
193 STATIC_ASSERT(
194 sizeof(HeapGraphEdge) ==
195 SnapshotSizeConstants<kPointerSize>::kExpectedHeapGraphEdgeSize);
196 STATIC_ASSERT(
197 sizeof(HeapEntry) ==
198 SnapshotSizeConstants<kPointerSize>::kExpectedHeapEntrySize);
199 USE(SnapshotSizeConstants<4>::kExpectedHeapGraphEdgeSize);
200 USE(SnapshotSizeConstants<4>::kExpectedHeapEntrySize);
201 USE(SnapshotSizeConstants<8>::kExpectedHeapGraphEdgeSize);
202 USE(SnapshotSizeConstants<8>::kExpectedHeapEntrySize);
203 for (int i = 0; i < VisitorSynchronization::kNumberOfSyncTags; ++i) {
204 gc_subroot_indexes_[i] = HeapEntry::kNoEntry;
205 }
206 }
207
208
Delete()209 void HeapSnapshot::Delete() {
210 profiler_->RemoveSnapshot(this);
211 delete this;
212 }
213
214
RememberLastJSObjectId()215 void HeapSnapshot::RememberLastJSObjectId() {
216 max_snapshot_js_object_id_ = profiler_->heap_object_map()->last_assigned_id();
217 }
218
219
AddRootEntry()220 HeapEntry* HeapSnapshot::AddRootEntry() {
221 ASSERT(root_index_ == HeapEntry::kNoEntry);
222 ASSERT(entries_.is_empty()); // Root entry must be the first one.
223 HeapEntry* entry = AddEntry(HeapEntry::kSynthetic,
224 "",
225 HeapObjectsMap::kInternalRootObjectId,
226 0,
227 0);
228 root_index_ = entry->index();
229 ASSERT(root_index_ == 0);
230 return entry;
231 }
232
233
AddGcRootsEntry()234 HeapEntry* HeapSnapshot::AddGcRootsEntry() {
235 ASSERT(gc_roots_index_ == HeapEntry::kNoEntry);
236 HeapEntry* entry = AddEntry(HeapEntry::kSynthetic,
237 "(GC roots)",
238 HeapObjectsMap::kGcRootsObjectId,
239 0,
240 0);
241 gc_roots_index_ = entry->index();
242 return entry;
243 }
244
245
AddGcSubrootEntry(int tag)246 HeapEntry* HeapSnapshot::AddGcSubrootEntry(int tag) {
247 ASSERT(gc_subroot_indexes_[tag] == HeapEntry::kNoEntry);
248 ASSERT(0 <= tag && tag < VisitorSynchronization::kNumberOfSyncTags);
249 HeapEntry* entry = AddEntry(
250 HeapEntry::kSynthetic,
251 VisitorSynchronization::kTagNames[tag],
252 HeapObjectsMap::GetNthGcSubrootId(tag),
253 0,
254 0);
255 gc_subroot_indexes_[tag] = entry->index();
256 return entry;
257 }
258
259
AddEntry(HeapEntry::Type type,const char * name,SnapshotObjectId id,size_t size,unsigned trace_node_id)260 HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
261 const char* name,
262 SnapshotObjectId id,
263 size_t size,
264 unsigned trace_node_id) {
265 HeapEntry entry(this, type, name, id, size, trace_node_id);
266 entries_.Add(entry);
267 return &entries_.last();
268 }
269
270
FillChildren()271 void HeapSnapshot::FillChildren() {
272 ASSERT(children().is_empty());
273 children().Allocate(edges().length());
274 int children_index = 0;
275 for (int i = 0; i < entries().length(); ++i) {
276 HeapEntry* entry = &entries()[i];
277 children_index = entry->set_children_index(children_index);
278 }
279 ASSERT(edges().length() == children_index);
280 for (int i = 0; i < edges().length(); ++i) {
281 HeapGraphEdge* edge = &edges()[i];
282 edge->ReplaceToIndexWithEntry(this);
283 edge->from()->add_child(edge);
284 }
285 }
286
287
288 class FindEntryById {
289 public:
FindEntryById(SnapshotObjectId id)290 explicit FindEntryById(SnapshotObjectId id) : id_(id) { }
operator ()(HeapEntry * const * entry)291 int operator()(HeapEntry* const* entry) {
292 if ((*entry)->id() == id_) return 0;
293 return (*entry)->id() < id_ ? -1 : 1;
294 }
295 private:
296 SnapshotObjectId id_;
297 };
298
299
GetEntryById(SnapshotObjectId id)300 HeapEntry* HeapSnapshot::GetEntryById(SnapshotObjectId id) {
301 List<HeapEntry*>* entries_by_id = GetSortedEntriesList();
302 // Perform a binary search by id.
303 int index = SortedListBSearch(*entries_by_id, FindEntryById(id));
304 if (index == -1)
305 return NULL;
306 return entries_by_id->at(index);
307 }
308
309
310 template<class T>
SortByIds(const T * entry1_ptr,const T * entry2_ptr)311 static int SortByIds(const T* entry1_ptr,
312 const T* entry2_ptr) {
313 if ((*entry1_ptr)->id() == (*entry2_ptr)->id()) return 0;
314 return (*entry1_ptr)->id() < (*entry2_ptr)->id() ? -1 : 1;
315 }
316
317
GetSortedEntriesList()318 List<HeapEntry*>* HeapSnapshot::GetSortedEntriesList() {
319 if (sorted_entries_.is_empty()) {
320 sorted_entries_.Allocate(entries_.length());
321 for (int i = 0; i < entries_.length(); ++i) {
322 sorted_entries_[i] = &entries_[i];
323 }
324 sorted_entries_.Sort(SortByIds);
325 }
326 return &sorted_entries_;
327 }
328
329
Print(int max_depth)330 void HeapSnapshot::Print(int max_depth) {
331 root()->Print("", "", max_depth, 0);
332 }
333
334
RawSnapshotSize() const335 size_t HeapSnapshot::RawSnapshotSize() const {
336 return
337 sizeof(*this) +
338 GetMemoryUsedByList(entries_) +
339 GetMemoryUsedByList(edges_) +
340 GetMemoryUsedByList(children_) +
341 GetMemoryUsedByList(sorted_entries_);
342 }
343
344
345 // We split IDs on evens for embedder objects (see
346 // HeapObjectsMap::GenerateId) and odds for native objects.
347 const SnapshotObjectId HeapObjectsMap::kInternalRootObjectId = 1;
348 const SnapshotObjectId HeapObjectsMap::kGcRootsObjectId =
349 HeapObjectsMap::kInternalRootObjectId + HeapObjectsMap::kObjectIdStep;
350 const SnapshotObjectId HeapObjectsMap::kGcRootsFirstSubrootId =
351 HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep;
352 const SnapshotObjectId HeapObjectsMap::kFirstAvailableObjectId =
353 HeapObjectsMap::kGcRootsFirstSubrootId +
354 VisitorSynchronization::kNumberOfSyncTags * HeapObjectsMap::kObjectIdStep;
355
356
AddressesMatch(void * key1,void * key2)357 static bool AddressesMatch(void* key1, void* key2) {
358 return key1 == key2;
359 }
360
361
HeapObjectsMap(Heap * heap)362 HeapObjectsMap::HeapObjectsMap(Heap* heap)
363 : next_id_(kFirstAvailableObjectId),
364 entries_map_(AddressesMatch),
365 heap_(heap) {
366 // This dummy element solves a problem with entries_map_.
367 // When we do lookup in HashMap we see no difference between two cases:
368 // it has an entry with NULL as the value or it has created
369 // a new entry on the fly with NULL as the default value.
370 // With such dummy element we have a guaranty that all entries_map_ entries
371 // will have the value field grater than 0.
372 // This fact is using in MoveObject method.
373 entries_.Add(EntryInfo(0, NULL, 0));
374 }
375
376
MoveObject(Address from,Address to,int object_size)377 bool HeapObjectsMap::MoveObject(Address from, Address to, int object_size) {
378 ASSERT(to != NULL);
379 ASSERT(from != NULL);
380 if (from == to) return false;
381 void* from_value = entries_map_.Remove(from, ComputePointerHash(from));
382 if (from_value == NULL) {
383 // It may occur that some untracked object moves to an address X and there
384 // is a tracked object at that address. In this case we should remove the
385 // entry as we know that the object has died.
386 void* to_value = entries_map_.Remove(to, ComputePointerHash(to));
387 if (to_value != NULL) {
388 int to_entry_info_index =
389 static_cast<int>(reinterpret_cast<intptr_t>(to_value));
390 entries_.at(to_entry_info_index).addr = NULL;
391 }
392 } else {
393 HashMap::Entry* to_entry = entries_map_.Lookup(to, ComputePointerHash(to),
394 true);
395 if (to_entry->value != NULL) {
396 // We found the existing entry with to address for an old object.
397 // Without this operation we will have two EntryInfo's with the same
398 // value in addr field. It is bad because later at RemoveDeadEntries
399 // one of this entry will be removed with the corresponding entries_map_
400 // entry.
401 int to_entry_info_index =
402 static_cast<int>(reinterpret_cast<intptr_t>(to_entry->value));
403 entries_.at(to_entry_info_index).addr = NULL;
404 }
405 int from_entry_info_index =
406 static_cast<int>(reinterpret_cast<intptr_t>(from_value));
407 entries_.at(from_entry_info_index).addr = to;
408 // Size of an object can change during its life, so to keep information
409 // about the object in entries_ consistent, we have to adjust size when the
410 // object is migrated.
411 if (FLAG_heap_profiler_trace_objects) {
412 PrintF("Move object from %p to %p old size %6d new size %6d\n",
413 from,
414 to,
415 entries_.at(from_entry_info_index).size,
416 object_size);
417 }
418 entries_.at(from_entry_info_index).size = object_size;
419 to_entry->value = from_value;
420 }
421 return from_value != NULL;
422 }
423
424
UpdateObjectSize(Address addr,int size)425 void HeapObjectsMap::UpdateObjectSize(Address addr, int size) {
426 FindOrAddEntry(addr, size, false);
427 }
428
429
FindEntry(Address addr)430 SnapshotObjectId HeapObjectsMap::FindEntry(Address addr) {
431 HashMap::Entry* entry = entries_map_.Lookup(addr, ComputePointerHash(addr),
432 false);
433 if (entry == NULL) return 0;
434 int entry_index = static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
435 EntryInfo& entry_info = entries_.at(entry_index);
436 ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
437 return entry_info.id;
438 }
439
440
FindOrAddEntry(Address addr,unsigned int size,bool accessed)441 SnapshotObjectId HeapObjectsMap::FindOrAddEntry(Address addr,
442 unsigned int size,
443 bool accessed) {
444 ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
445 HashMap::Entry* entry = entries_map_.Lookup(addr, ComputePointerHash(addr),
446 true);
447 if (entry->value != NULL) {
448 int entry_index =
449 static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
450 EntryInfo& entry_info = entries_.at(entry_index);
451 entry_info.accessed = accessed;
452 if (FLAG_heap_profiler_trace_objects) {
453 PrintF("Update object size : %p with old size %d and new size %d\n",
454 addr,
455 entry_info.size,
456 size);
457 }
458 entry_info.size = size;
459 return entry_info.id;
460 }
461 entry->value = reinterpret_cast<void*>(entries_.length());
462 SnapshotObjectId id = next_id_;
463 next_id_ += kObjectIdStep;
464 entries_.Add(EntryInfo(id, addr, size, accessed));
465 ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
466 return id;
467 }
468
469
StopHeapObjectsTracking()470 void HeapObjectsMap::StopHeapObjectsTracking() {
471 time_intervals_.Clear();
472 }
473
474
UpdateHeapObjectsMap()475 void HeapObjectsMap::UpdateHeapObjectsMap() {
476 if (FLAG_heap_profiler_trace_objects) {
477 PrintF("Begin HeapObjectsMap::UpdateHeapObjectsMap. map has %d entries.\n",
478 entries_map_.occupancy());
479 }
480 heap_->CollectAllGarbage(Heap::kMakeHeapIterableMask,
481 "HeapObjectsMap::UpdateHeapObjectsMap");
482 HeapIterator iterator(heap_);
483 for (HeapObject* obj = iterator.next();
484 obj != NULL;
485 obj = iterator.next()) {
486 FindOrAddEntry(obj->address(), obj->Size());
487 if (FLAG_heap_profiler_trace_objects) {
488 PrintF("Update object : %p %6d. Next address is %p\n",
489 obj->address(),
490 obj->Size(),
491 obj->address() + obj->Size());
492 }
493 }
494 RemoveDeadEntries();
495 if (FLAG_heap_profiler_trace_objects) {
496 PrintF("End HeapObjectsMap::UpdateHeapObjectsMap. map has %d entries.\n",
497 entries_map_.occupancy());
498 }
499 }
500
501
502 namespace {
503
504
505 struct HeapObjectInfo {
HeapObjectInfov8::internal::__anoncc780fe60211::HeapObjectInfo506 HeapObjectInfo(HeapObject* obj, int expected_size)
507 : obj(obj),
508 expected_size(expected_size) {
509 }
510
511 HeapObject* obj;
512 int expected_size;
513
IsValidv8::internal::__anoncc780fe60211::HeapObjectInfo514 bool IsValid() const { return expected_size == obj->Size(); }
515
Printv8::internal::__anoncc780fe60211::HeapObjectInfo516 void Print() const {
517 if (expected_size == 0) {
518 PrintF("Untracked object : %p %6d. Next address is %p\n",
519 obj->address(),
520 obj->Size(),
521 obj->address() + obj->Size());
522 } else if (obj->Size() != expected_size) {
523 PrintF("Wrong size %6d: %p %6d. Next address is %p\n",
524 expected_size,
525 obj->address(),
526 obj->Size(),
527 obj->address() + obj->Size());
528 } else {
529 PrintF("Good object : %p %6d. Next address is %p\n",
530 obj->address(),
531 expected_size,
532 obj->address() + obj->Size());
533 }
534 }
535 };
536
537
comparator(const HeapObjectInfo * a,const HeapObjectInfo * b)538 static int comparator(const HeapObjectInfo* a, const HeapObjectInfo* b) {
539 if (a->obj < b->obj) return -1;
540 if (a->obj > b->obj) return 1;
541 return 0;
542 }
543
544
545 } // namespace
546
547
FindUntrackedObjects()548 int HeapObjectsMap::FindUntrackedObjects() {
549 List<HeapObjectInfo> heap_objects(1000);
550
551 HeapIterator iterator(heap_);
552 int untracked = 0;
553 for (HeapObject* obj = iterator.next();
554 obj != NULL;
555 obj = iterator.next()) {
556 HashMap::Entry* entry = entries_map_.Lookup(
557 obj->address(), ComputePointerHash(obj->address()), false);
558 if (entry == NULL) {
559 ++untracked;
560 if (FLAG_heap_profiler_trace_objects) {
561 heap_objects.Add(HeapObjectInfo(obj, 0));
562 }
563 } else {
564 int entry_index = static_cast<int>(
565 reinterpret_cast<intptr_t>(entry->value));
566 EntryInfo& entry_info = entries_.at(entry_index);
567 if (FLAG_heap_profiler_trace_objects) {
568 heap_objects.Add(HeapObjectInfo(obj,
569 static_cast<int>(entry_info.size)));
570 if (obj->Size() != static_cast<int>(entry_info.size))
571 ++untracked;
572 } else {
573 CHECK_EQ(obj->Size(), static_cast<int>(entry_info.size));
574 }
575 }
576 }
577 if (FLAG_heap_profiler_trace_objects) {
578 PrintF("\nBegin HeapObjectsMap::FindUntrackedObjects. %d entries in map.\n",
579 entries_map_.occupancy());
580 heap_objects.Sort(comparator);
581 int last_printed_object = -1;
582 bool print_next_object = false;
583 for (int i = 0; i < heap_objects.length(); ++i) {
584 const HeapObjectInfo& object_info = heap_objects[i];
585 if (!object_info.IsValid()) {
586 ++untracked;
587 if (last_printed_object != i - 1) {
588 if (i > 0) {
589 PrintF("%d objects were skipped\n", i - 1 - last_printed_object);
590 heap_objects[i - 1].Print();
591 }
592 }
593 object_info.Print();
594 last_printed_object = i;
595 print_next_object = true;
596 } else if (print_next_object) {
597 object_info.Print();
598 print_next_object = false;
599 last_printed_object = i;
600 }
601 }
602 if (last_printed_object < heap_objects.length() - 1) {
603 PrintF("Last %d objects were skipped\n",
604 heap_objects.length() - 1 - last_printed_object);
605 }
606 PrintF("End HeapObjectsMap::FindUntrackedObjects. %d entries in map.\n\n",
607 entries_map_.occupancy());
608 }
609 return untracked;
610 }
611
612
PushHeapObjectsStats(OutputStream * stream)613 SnapshotObjectId HeapObjectsMap::PushHeapObjectsStats(OutputStream* stream) {
614 UpdateHeapObjectsMap();
615 time_intervals_.Add(TimeInterval(next_id_));
616 int prefered_chunk_size = stream->GetChunkSize();
617 List<v8::HeapStatsUpdate> stats_buffer;
618 ASSERT(!entries_.is_empty());
619 EntryInfo* entry_info = &entries_.first();
620 EntryInfo* end_entry_info = &entries_.last() + 1;
621 for (int time_interval_index = 0;
622 time_interval_index < time_intervals_.length();
623 ++time_interval_index) {
624 TimeInterval& time_interval = time_intervals_[time_interval_index];
625 SnapshotObjectId time_interval_id = time_interval.id;
626 uint32_t entries_size = 0;
627 EntryInfo* start_entry_info = entry_info;
628 while (entry_info < end_entry_info && entry_info->id < time_interval_id) {
629 entries_size += entry_info->size;
630 ++entry_info;
631 }
632 uint32_t entries_count =
633 static_cast<uint32_t>(entry_info - start_entry_info);
634 if (time_interval.count != entries_count ||
635 time_interval.size != entries_size) {
636 stats_buffer.Add(v8::HeapStatsUpdate(
637 time_interval_index,
638 time_interval.count = entries_count,
639 time_interval.size = entries_size));
640 if (stats_buffer.length() >= prefered_chunk_size) {
641 OutputStream::WriteResult result = stream->WriteHeapStatsChunk(
642 &stats_buffer.first(), stats_buffer.length());
643 if (result == OutputStream::kAbort) return last_assigned_id();
644 stats_buffer.Clear();
645 }
646 }
647 }
648 ASSERT(entry_info == end_entry_info);
649 if (!stats_buffer.is_empty()) {
650 OutputStream::WriteResult result = stream->WriteHeapStatsChunk(
651 &stats_buffer.first(), stats_buffer.length());
652 if (result == OutputStream::kAbort) return last_assigned_id();
653 }
654 stream->EndOfStream();
655 return last_assigned_id();
656 }
657
658
RemoveDeadEntries()659 void HeapObjectsMap::RemoveDeadEntries() {
660 ASSERT(entries_.length() > 0 &&
661 entries_.at(0).id == 0 &&
662 entries_.at(0).addr == NULL);
663 int first_free_entry = 1;
664 for (int i = 1; i < entries_.length(); ++i) {
665 EntryInfo& entry_info = entries_.at(i);
666 if (entry_info.accessed) {
667 if (first_free_entry != i) {
668 entries_.at(first_free_entry) = entry_info;
669 }
670 entries_.at(first_free_entry).accessed = false;
671 HashMap::Entry* entry = entries_map_.Lookup(
672 entry_info.addr, ComputePointerHash(entry_info.addr), false);
673 ASSERT(entry);
674 entry->value = reinterpret_cast<void*>(first_free_entry);
675 ++first_free_entry;
676 } else {
677 if (entry_info.addr) {
678 entries_map_.Remove(entry_info.addr,
679 ComputePointerHash(entry_info.addr));
680 }
681 }
682 }
683 entries_.Rewind(first_free_entry);
684 ASSERT(static_cast<uint32_t>(entries_.length()) - 1 ==
685 entries_map_.occupancy());
686 }
687
688
GenerateId(v8::RetainedObjectInfo * info)689 SnapshotObjectId HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) {
690 SnapshotObjectId id = static_cast<SnapshotObjectId>(info->GetHash());
691 const char* label = info->GetLabel();
692 id ^= StringHasher::HashSequentialString(label,
693 static_cast<int>(strlen(label)),
694 heap_->HashSeed());
695 intptr_t element_count = info->GetElementCount();
696 if (element_count != -1)
697 id ^= ComputeIntegerHash(static_cast<uint32_t>(element_count),
698 v8::internal::kZeroHashSeed);
699 return id << 1;
700 }
701
702
GetUsedMemorySize() const703 size_t HeapObjectsMap::GetUsedMemorySize() const {
704 return
705 sizeof(*this) +
706 sizeof(HashMap::Entry) * entries_map_.capacity() +
707 GetMemoryUsedByList(entries_) +
708 GetMemoryUsedByList(time_intervals_);
709 }
710
711
HeapEntriesMap()712 HeapEntriesMap::HeapEntriesMap()
713 : entries_(HashMap::PointersMatch) {
714 }
715
716
Map(HeapThing thing)717 int HeapEntriesMap::Map(HeapThing thing) {
718 HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), false);
719 if (cache_entry == NULL) return HeapEntry::kNoEntry;
720 return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
721 }
722
723
Pair(HeapThing thing,int entry)724 void HeapEntriesMap::Pair(HeapThing thing, int entry) {
725 HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), true);
726 ASSERT(cache_entry->value == NULL);
727 cache_entry->value = reinterpret_cast<void*>(static_cast<intptr_t>(entry));
728 }
729
730
HeapObjectsSet()731 HeapObjectsSet::HeapObjectsSet()
732 : entries_(HashMap::PointersMatch) {
733 }
734
735
Clear()736 void HeapObjectsSet::Clear() {
737 entries_.Clear();
738 }
739
740
Contains(Object * obj)741 bool HeapObjectsSet::Contains(Object* obj) {
742 if (!obj->IsHeapObject()) return false;
743 HeapObject* object = HeapObject::cast(obj);
744 return entries_.Lookup(object, HeapEntriesMap::Hash(object), false) != NULL;
745 }
746
747
Insert(Object * obj)748 void HeapObjectsSet::Insert(Object* obj) {
749 if (!obj->IsHeapObject()) return;
750 HeapObject* object = HeapObject::cast(obj);
751 entries_.Lookup(object, HeapEntriesMap::Hash(object), true);
752 }
753
754
GetTag(Object * obj)755 const char* HeapObjectsSet::GetTag(Object* obj) {
756 HeapObject* object = HeapObject::cast(obj);
757 HashMap::Entry* cache_entry =
758 entries_.Lookup(object, HeapEntriesMap::Hash(object), false);
759 return cache_entry != NULL
760 ? reinterpret_cast<const char*>(cache_entry->value)
761 : NULL;
762 }
763
764
SetTag(Object * obj,const char * tag)765 void HeapObjectsSet::SetTag(Object* obj, const char* tag) {
766 if (!obj->IsHeapObject()) return;
767 HeapObject* object = HeapObject::cast(obj);
768 HashMap::Entry* cache_entry =
769 entries_.Lookup(object, HeapEntriesMap::Hash(object), true);
770 cache_entry->value = const_cast<char*>(tag);
771 }
772
773
774 HeapObject* const V8HeapExplorer::kInternalRootObject =
775 reinterpret_cast<HeapObject*>(
776 static_cast<intptr_t>(HeapObjectsMap::kInternalRootObjectId));
777 HeapObject* const V8HeapExplorer::kGcRootsObject =
778 reinterpret_cast<HeapObject*>(
779 static_cast<intptr_t>(HeapObjectsMap::kGcRootsObjectId));
780 HeapObject* const V8HeapExplorer::kFirstGcSubrootObject =
781 reinterpret_cast<HeapObject*>(
782 static_cast<intptr_t>(HeapObjectsMap::kGcRootsFirstSubrootId));
783 HeapObject* const V8HeapExplorer::kLastGcSubrootObject =
784 reinterpret_cast<HeapObject*>(
785 static_cast<intptr_t>(HeapObjectsMap::kFirstAvailableObjectId));
786
787
V8HeapExplorer(HeapSnapshot * snapshot,SnapshottingProgressReportingInterface * progress,v8::HeapProfiler::ObjectNameResolver * resolver)788 V8HeapExplorer::V8HeapExplorer(
789 HeapSnapshot* snapshot,
790 SnapshottingProgressReportingInterface* progress,
791 v8::HeapProfiler::ObjectNameResolver* resolver)
792 : heap_(snapshot->profiler()->heap_object_map()->heap()),
793 snapshot_(snapshot),
794 names_(snapshot_->profiler()->names()),
795 heap_object_map_(snapshot_->profiler()->heap_object_map()),
796 progress_(progress),
797 filler_(NULL),
798 global_object_name_resolver_(resolver) {
799 }
800
801
~V8HeapExplorer()802 V8HeapExplorer::~V8HeapExplorer() {
803 }
804
805
AllocateEntry(HeapThing ptr)806 HeapEntry* V8HeapExplorer::AllocateEntry(HeapThing ptr) {
807 return AddEntry(reinterpret_cast<HeapObject*>(ptr));
808 }
809
810
AddEntry(HeapObject * object)811 HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object) {
812 if (object == kInternalRootObject) {
813 snapshot_->AddRootEntry();
814 return snapshot_->root();
815 } else if (object == kGcRootsObject) {
816 HeapEntry* entry = snapshot_->AddGcRootsEntry();
817 return entry;
818 } else if (object >= kFirstGcSubrootObject && object < kLastGcSubrootObject) {
819 HeapEntry* entry = snapshot_->AddGcSubrootEntry(GetGcSubrootOrder(object));
820 return entry;
821 } else if (object->IsJSFunction()) {
822 JSFunction* func = JSFunction::cast(object);
823 SharedFunctionInfo* shared = func->shared();
824 const char* name = shared->bound() ? "native_bind" :
825 names_->GetName(String::cast(shared->name()));
826 return AddEntry(object, HeapEntry::kClosure, name);
827 } else if (object->IsJSRegExp()) {
828 JSRegExp* re = JSRegExp::cast(object);
829 return AddEntry(object,
830 HeapEntry::kRegExp,
831 names_->GetName(re->Pattern()));
832 } else if (object->IsJSObject()) {
833 const char* name = names_->GetName(
834 GetConstructorName(JSObject::cast(object)));
835 if (object->IsJSGlobalObject()) {
836 const char* tag = objects_tags_.GetTag(object);
837 if (tag != NULL) {
838 name = names_->GetFormatted("%s / %s", name, tag);
839 }
840 }
841 return AddEntry(object, HeapEntry::kObject, name);
842 } else if (object->IsString()) {
843 String* string = String::cast(object);
844 if (string->IsConsString())
845 return AddEntry(object,
846 HeapEntry::kConsString,
847 "(concatenated string)");
848 if (string->IsSlicedString())
849 return AddEntry(object,
850 HeapEntry::kSlicedString,
851 "(sliced string)");
852 return AddEntry(object,
853 HeapEntry::kString,
854 names_->GetName(String::cast(object)));
855 } else if (object->IsSymbol()) {
856 return AddEntry(object, HeapEntry::kSymbol, "symbol");
857 } else if (object->IsCode()) {
858 return AddEntry(object, HeapEntry::kCode, "");
859 } else if (object->IsSharedFunctionInfo()) {
860 String* name = String::cast(SharedFunctionInfo::cast(object)->name());
861 return AddEntry(object,
862 HeapEntry::kCode,
863 names_->GetName(name));
864 } else if (object->IsScript()) {
865 Object* name = Script::cast(object)->name();
866 return AddEntry(object,
867 HeapEntry::kCode,
868 name->IsString()
869 ? names_->GetName(String::cast(name))
870 : "");
871 } else if (object->IsNativeContext()) {
872 return AddEntry(object, HeapEntry::kHidden, "system / NativeContext");
873 } else if (object->IsContext()) {
874 return AddEntry(object, HeapEntry::kObject, "system / Context");
875 } else if (object->IsFixedArray() ||
876 object->IsFixedDoubleArray() ||
877 object->IsByteArray() ||
878 object->IsExternalArray()) {
879 return AddEntry(object, HeapEntry::kArray, "");
880 } else if (object->IsHeapNumber()) {
881 return AddEntry(object, HeapEntry::kHeapNumber, "number");
882 }
883 return AddEntry(object, HeapEntry::kHidden, GetSystemEntryName(object));
884 }
885
886
AddEntry(HeapObject * object,HeapEntry::Type type,const char * name)887 HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
888 HeapEntry::Type type,
889 const char* name) {
890 return AddEntry(object->address(), type, name, object->Size());
891 }
892
893
AddEntry(Address address,HeapEntry::Type type,const char * name,size_t size)894 HeapEntry* V8HeapExplorer::AddEntry(Address address,
895 HeapEntry::Type type,
896 const char* name,
897 size_t size) {
898 SnapshotObjectId object_id = heap_object_map_->FindOrAddEntry(
899 address, static_cast<unsigned int>(size));
900 unsigned trace_node_id = 0;
901 if (AllocationTracker* allocation_tracker =
902 snapshot_->profiler()->allocation_tracker()) {
903 trace_node_id =
904 allocation_tracker->address_to_trace()->GetTraceNodeId(address);
905 }
906 return snapshot_->AddEntry(type, name, object_id, size, trace_node_id);
907 }
908
909
910 class SnapshotFiller {
911 public:
SnapshotFiller(HeapSnapshot * snapshot,HeapEntriesMap * entries)912 explicit SnapshotFiller(HeapSnapshot* snapshot, HeapEntriesMap* entries)
913 : snapshot_(snapshot),
914 names_(snapshot->profiler()->names()),
915 entries_(entries) { }
AddEntry(HeapThing ptr,HeapEntriesAllocator * allocator)916 HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
917 HeapEntry* entry = allocator->AllocateEntry(ptr);
918 entries_->Pair(ptr, entry->index());
919 return entry;
920 }
FindEntry(HeapThing ptr)921 HeapEntry* FindEntry(HeapThing ptr) {
922 int index = entries_->Map(ptr);
923 return index != HeapEntry::kNoEntry ? &snapshot_->entries()[index] : NULL;
924 }
FindOrAddEntry(HeapThing ptr,HeapEntriesAllocator * allocator)925 HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
926 HeapEntry* entry = FindEntry(ptr);
927 return entry != NULL ? entry : AddEntry(ptr, allocator);
928 }
SetIndexedReference(HeapGraphEdge::Type type,int parent,int index,HeapEntry * child_entry)929 void SetIndexedReference(HeapGraphEdge::Type type,
930 int parent,
931 int index,
932 HeapEntry* child_entry) {
933 HeapEntry* parent_entry = &snapshot_->entries()[parent];
934 parent_entry->SetIndexedReference(type, index, child_entry);
935 }
SetIndexedAutoIndexReference(HeapGraphEdge::Type type,int parent,HeapEntry * child_entry)936 void SetIndexedAutoIndexReference(HeapGraphEdge::Type type,
937 int parent,
938 HeapEntry* child_entry) {
939 HeapEntry* parent_entry = &snapshot_->entries()[parent];
940 int index = parent_entry->children_count() + 1;
941 parent_entry->SetIndexedReference(type, index, child_entry);
942 }
SetNamedReference(HeapGraphEdge::Type type,int parent,const char * reference_name,HeapEntry * child_entry)943 void SetNamedReference(HeapGraphEdge::Type type,
944 int parent,
945 const char* reference_name,
946 HeapEntry* child_entry) {
947 HeapEntry* parent_entry = &snapshot_->entries()[parent];
948 parent_entry->SetNamedReference(type, reference_name, child_entry);
949 }
SetNamedAutoIndexReference(HeapGraphEdge::Type type,int parent,HeapEntry * child_entry)950 void SetNamedAutoIndexReference(HeapGraphEdge::Type type,
951 int parent,
952 HeapEntry* child_entry) {
953 HeapEntry* parent_entry = &snapshot_->entries()[parent];
954 int index = parent_entry->children_count() + 1;
955 parent_entry->SetNamedReference(
956 type,
957 names_->GetName(index),
958 child_entry);
959 }
960
961 private:
962 HeapSnapshot* snapshot_;
963 StringsStorage* names_;
964 HeapEntriesMap* entries_;
965 };
966
967
968 class GcSubrootsEnumerator : public ObjectVisitor {
969 public:
GcSubrootsEnumerator(SnapshotFiller * filler,V8HeapExplorer * explorer)970 GcSubrootsEnumerator(
971 SnapshotFiller* filler, V8HeapExplorer* explorer)
972 : filler_(filler),
973 explorer_(explorer),
974 previous_object_count_(0),
975 object_count_(0) {
976 }
VisitPointers(Object ** start,Object ** end)977 void VisitPointers(Object** start, Object** end) {
978 object_count_ += end - start;
979 }
Synchronize(VisitorSynchronization::SyncTag tag)980 void Synchronize(VisitorSynchronization::SyncTag tag) {
981 // Skip empty subroots.
982 if (previous_object_count_ != object_count_) {
983 previous_object_count_ = object_count_;
984 filler_->AddEntry(V8HeapExplorer::GetNthGcSubrootObject(tag), explorer_);
985 }
986 }
987 private:
988 SnapshotFiller* filler_;
989 V8HeapExplorer* explorer_;
990 intptr_t previous_object_count_;
991 intptr_t object_count_;
992 };
993
994
AddRootEntries(SnapshotFiller * filler)995 void V8HeapExplorer::AddRootEntries(SnapshotFiller* filler) {
996 filler->AddEntry(kInternalRootObject, this);
997 filler->AddEntry(kGcRootsObject, this);
998 GcSubrootsEnumerator enumerator(filler, this);
999 heap_->IterateRoots(&enumerator, VISIT_ALL);
1000 }
1001
1002
GetSystemEntryName(HeapObject * object)1003 const char* V8HeapExplorer::GetSystemEntryName(HeapObject* object) {
1004 switch (object->map()->instance_type()) {
1005 case MAP_TYPE:
1006 switch (Map::cast(object)->instance_type()) {
1007 #define MAKE_STRING_MAP_CASE(instance_type, size, name, Name) \
1008 case instance_type: return "system / Map (" #Name ")";
1009 STRING_TYPE_LIST(MAKE_STRING_MAP_CASE)
1010 #undef MAKE_STRING_MAP_CASE
1011 default: return "system / Map";
1012 }
1013 case CELL_TYPE: return "system / Cell";
1014 case PROPERTY_CELL_TYPE: return "system / PropertyCell";
1015 case FOREIGN_TYPE: return "system / Foreign";
1016 case ODDBALL_TYPE: return "system / Oddball";
1017 #define MAKE_STRUCT_CASE(NAME, Name, name) \
1018 case NAME##_TYPE: return "system / "#Name;
1019 STRUCT_LIST(MAKE_STRUCT_CASE)
1020 #undef MAKE_STRUCT_CASE
1021 default: return "system";
1022 }
1023 }
1024
1025
EstimateObjectsCount(HeapIterator * iterator)1026 int V8HeapExplorer::EstimateObjectsCount(HeapIterator* iterator) {
1027 int objects_count = 0;
1028 for (HeapObject* obj = iterator->next();
1029 obj != NULL;
1030 obj = iterator->next()) {
1031 objects_count++;
1032 }
1033 return objects_count;
1034 }
1035
1036
1037 class IndexedReferencesExtractor : public ObjectVisitor {
1038 public:
IndexedReferencesExtractor(V8HeapExplorer * generator,HeapObject * parent_obj,int parent)1039 IndexedReferencesExtractor(V8HeapExplorer* generator,
1040 HeapObject* parent_obj,
1041 int parent)
1042 : generator_(generator),
1043 parent_obj_(parent_obj),
1044 parent_(parent),
1045 next_index_(0) {
1046 }
VisitCodeEntry(Address entry_address)1047 void VisitCodeEntry(Address entry_address) {
1048 Code* code = Code::cast(Code::GetObjectFromEntryAddress(entry_address));
1049 generator_->SetInternalReference(parent_obj_, parent_, "code", code);
1050 generator_->TagCodeObject(code);
1051 }
VisitPointers(Object ** start,Object ** end)1052 void VisitPointers(Object** start, Object** end) {
1053 for (Object** p = start; p < end; p++) {
1054 ++next_index_;
1055 if (CheckVisitedAndUnmark(p)) continue;
1056 generator_->SetHiddenReference(parent_obj_, parent_, next_index_, *p);
1057 }
1058 }
MarkVisitedField(HeapObject * obj,int offset)1059 static void MarkVisitedField(HeapObject* obj, int offset) {
1060 if (offset < 0) return;
1061 Address field = obj->address() + offset;
1062 ASSERT(Memory::Object_at(field)->IsHeapObject());
1063 intptr_t p = reinterpret_cast<intptr_t>(Memory::Object_at(field));
1064 ASSERT(!IsMarked(p));
1065 intptr_t p_tagged = p | kTag;
1066 Memory::Object_at(field) = reinterpret_cast<Object*>(p_tagged);
1067 }
1068
1069 private:
CheckVisitedAndUnmark(Object ** field)1070 bool CheckVisitedAndUnmark(Object** field) {
1071 intptr_t p = reinterpret_cast<intptr_t>(*field);
1072 if (IsMarked(p)) {
1073 intptr_t p_untagged = (p & ~kTaggingMask) | kHeapObjectTag;
1074 *field = reinterpret_cast<Object*>(p_untagged);
1075 ASSERT((*field)->IsHeapObject());
1076 return true;
1077 }
1078 return false;
1079 }
1080
1081 static const intptr_t kTaggingMask = 3;
1082 static const intptr_t kTag = 3;
1083
IsMarked(intptr_t p)1084 static bool IsMarked(intptr_t p) { return (p & kTaggingMask) == kTag; }
1085
1086 V8HeapExplorer* generator_;
1087 HeapObject* parent_obj_;
1088 int parent_;
1089 int next_index_;
1090 };
1091
1092
ExtractReferencesPass1(int entry,HeapObject * obj)1093 bool V8HeapExplorer::ExtractReferencesPass1(int entry, HeapObject* obj) {
1094 if (obj->IsFixedArray()) return false; // FixedArrays are processed on pass 2
1095
1096 if (obj->IsJSGlobalProxy()) {
1097 ExtractJSGlobalProxyReferences(entry, JSGlobalProxy::cast(obj));
1098 } else if (obj->IsJSArrayBuffer()) {
1099 ExtractJSArrayBufferReferences(entry, JSArrayBuffer::cast(obj));
1100 } else if (obj->IsJSWeakSet()) {
1101 ExtractJSWeakCollectionReferences(entry, JSWeakSet::cast(obj));
1102 } else if (obj->IsJSWeakMap()) {
1103 ExtractJSWeakCollectionReferences(entry, JSWeakMap::cast(obj));
1104 } else if (obj->IsJSObject()) {
1105 ExtractJSObjectReferences(entry, JSObject::cast(obj));
1106 } else if (obj->IsString()) {
1107 ExtractStringReferences(entry, String::cast(obj));
1108 } else if (obj->IsSymbol()) {
1109 ExtractSymbolReferences(entry, Symbol::cast(obj));
1110 } else if (obj->IsMap()) {
1111 ExtractMapReferences(entry, Map::cast(obj));
1112 } else if (obj->IsSharedFunctionInfo()) {
1113 ExtractSharedFunctionInfoReferences(entry, SharedFunctionInfo::cast(obj));
1114 } else if (obj->IsScript()) {
1115 ExtractScriptReferences(entry, Script::cast(obj));
1116 } else if (obj->IsAccessorPair()) {
1117 ExtractAccessorPairReferences(entry, AccessorPair::cast(obj));
1118 } else if (obj->IsCodeCache()) {
1119 ExtractCodeCacheReferences(entry, CodeCache::cast(obj));
1120 } else if (obj->IsCode()) {
1121 ExtractCodeReferences(entry, Code::cast(obj));
1122 } else if (obj->IsBox()) {
1123 ExtractBoxReferences(entry, Box::cast(obj));
1124 } else if (obj->IsCell()) {
1125 ExtractCellReferences(entry, Cell::cast(obj));
1126 } else if (obj->IsPropertyCell()) {
1127 ExtractPropertyCellReferences(entry, PropertyCell::cast(obj));
1128 } else if (obj->IsAllocationSite()) {
1129 ExtractAllocationSiteReferences(entry, AllocationSite::cast(obj));
1130 }
1131 return true;
1132 }
1133
1134
ExtractReferencesPass2(int entry,HeapObject * obj)1135 bool V8HeapExplorer::ExtractReferencesPass2(int entry, HeapObject* obj) {
1136 if (!obj->IsFixedArray()) return false;
1137
1138 if (obj->IsContext()) {
1139 ExtractContextReferences(entry, Context::cast(obj));
1140 } else {
1141 ExtractFixedArrayReferences(entry, FixedArray::cast(obj));
1142 }
1143 return true;
1144 }
1145
1146
ExtractJSGlobalProxyReferences(int entry,JSGlobalProxy * proxy)1147 void V8HeapExplorer::ExtractJSGlobalProxyReferences(
1148 int entry, JSGlobalProxy* proxy) {
1149 SetInternalReference(proxy, entry,
1150 "native_context", proxy->native_context(),
1151 JSGlobalProxy::kNativeContextOffset);
1152 }
1153
1154
ExtractJSObjectReferences(int entry,JSObject * js_obj)1155 void V8HeapExplorer::ExtractJSObjectReferences(
1156 int entry, JSObject* js_obj) {
1157 HeapObject* obj = js_obj;
1158 ExtractClosureReferences(js_obj, entry);
1159 ExtractPropertyReferences(js_obj, entry);
1160 ExtractElementReferences(js_obj, entry);
1161 ExtractInternalReferences(js_obj, entry);
1162 SetPropertyReference(
1163 obj, entry, heap_->proto_string(), js_obj->GetPrototype());
1164 if (obj->IsJSFunction()) {
1165 JSFunction* js_fun = JSFunction::cast(js_obj);
1166 Object* proto_or_map = js_fun->prototype_or_initial_map();
1167 if (!proto_or_map->IsTheHole()) {
1168 if (!proto_or_map->IsMap()) {
1169 SetPropertyReference(
1170 obj, entry,
1171 heap_->prototype_string(), proto_or_map,
1172 NULL,
1173 JSFunction::kPrototypeOrInitialMapOffset);
1174 } else {
1175 SetPropertyReference(
1176 obj, entry,
1177 heap_->prototype_string(), js_fun->prototype());
1178 SetInternalReference(
1179 obj, entry, "initial_map", proto_or_map,
1180 JSFunction::kPrototypeOrInitialMapOffset);
1181 }
1182 }
1183 SharedFunctionInfo* shared_info = js_fun->shared();
1184 // JSFunction has either bindings or literals and never both.
1185 bool bound = shared_info->bound();
1186 TagObject(js_fun->literals_or_bindings(),
1187 bound ? "(function bindings)" : "(function literals)");
1188 SetInternalReference(js_fun, entry,
1189 bound ? "bindings" : "literals",
1190 js_fun->literals_or_bindings(),
1191 JSFunction::kLiteralsOffset);
1192 TagObject(shared_info, "(shared function info)");
1193 SetInternalReference(js_fun, entry,
1194 "shared", shared_info,
1195 JSFunction::kSharedFunctionInfoOffset);
1196 TagObject(js_fun->context(), "(context)");
1197 SetInternalReference(js_fun, entry,
1198 "context", js_fun->context(),
1199 JSFunction::kContextOffset);
1200 SetWeakReference(js_fun, entry,
1201 "next_function_link", js_fun->next_function_link(),
1202 JSFunction::kNextFunctionLinkOffset);
1203 STATIC_ASSERT(JSFunction::kNextFunctionLinkOffset
1204 == JSFunction::kNonWeakFieldsEndOffset);
1205 STATIC_ASSERT(JSFunction::kNextFunctionLinkOffset + kPointerSize
1206 == JSFunction::kSize);
1207 } else if (obj->IsGlobalObject()) {
1208 GlobalObject* global_obj = GlobalObject::cast(obj);
1209 SetInternalReference(global_obj, entry,
1210 "builtins", global_obj->builtins(),
1211 GlobalObject::kBuiltinsOffset);
1212 SetInternalReference(global_obj, entry,
1213 "native_context", global_obj->native_context(),
1214 GlobalObject::kNativeContextOffset);
1215 SetInternalReference(global_obj, entry,
1216 "global_context", global_obj->global_context(),
1217 GlobalObject::kGlobalContextOffset);
1218 SetInternalReference(global_obj, entry,
1219 "global_receiver", global_obj->global_receiver(),
1220 GlobalObject::kGlobalReceiverOffset);
1221 STATIC_ASSERT(GlobalObject::kHeaderSize - JSObject::kHeaderSize ==
1222 4 * kPointerSize);
1223 } else if (obj->IsJSArrayBufferView()) {
1224 JSArrayBufferView* view = JSArrayBufferView::cast(obj);
1225 SetInternalReference(view, entry, "buffer", view->buffer(),
1226 JSArrayBufferView::kBufferOffset);
1227 SetWeakReference(view, entry, "weak_next", view->weak_next(),
1228 JSArrayBufferView::kWeakNextOffset);
1229 }
1230 TagObject(js_obj->properties(), "(object properties)");
1231 SetInternalReference(obj, entry,
1232 "properties", js_obj->properties(),
1233 JSObject::kPropertiesOffset);
1234 TagObject(js_obj->elements(), "(object elements)");
1235 SetInternalReference(obj, entry,
1236 "elements", js_obj->elements(),
1237 JSObject::kElementsOffset);
1238 }
1239
1240
ExtractStringReferences(int entry,String * string)1241 void V8HeapExplorer::ExtractStringReferences(int entry, String* string) {
1242 if (string->IsConsString()) {
1243 ConsString* cs = ConsString::cast(string);
1244 SetInternalReference(cs, entry, "first", cs->first(),
1245 ConsString::kFirstOffset);
1246 SetInternalReference(cs, entry, "second", cs->second(),
1247 ConsString::kSecondOffset);
1248 } else if (string->IsSlicedString()) {
1249 SlicedString* ss = SlicedString::cast(string);
1250 SetInternalReference(ss, entry, "parent", ss->parent(),
1251 SlicedString::kParentOffset);
1252 }
1253 }
1254
1255
ExtractSymbolReferences(int entry,Symbol * symbol)1256 void V8HeapExplorer::ExtractSymbolReferences(int entry, Symbol* symbol) {
1257 SetInternalReference(symbol, entry,
1258 "name", symbol->name(),
1259 Symbol::kNameOffset);
1260 }
1261
1262
ExtractJSWeakCollectionReferences(int entry,JSWeakCollection * collection)1263 void V8HeapExplorer::ExtractJSWeakCollectionReferences(
1264 int entry, JSWeakCollection* collection) {
1265 MarkAsWeakContainer(collection->table());
1266 SetInternalReference(collection, entry,
1267 "table", collection->table(),
1268 JSWeakCollection::kTableOffset);
1269 }
1270
1271
ExtractContextReferences(int entry,Context * context)1272 void V8HeapExplorer::ExtractContextReferences(int entry, Context* context) {
1273 if (context == context->declaration_context()) {
1274 ScopeInfo* scope_info = context->closure()->shared()->scope_info();
1275 // Add context allocated locals.
1276 int context_locals = scope_info->ContextLocalCount();
1277 for (int i = 0; i < context_locals; ++i) {
1278 String* local_name = scope_info->ContextLocalName(i);
1279 int idx = Context::MIN_CONTEXT_SLOTS + i;
1280 SetContextReference(context, entry, local_name, context->get(idx),
1281 Context::OffsetOfElementAt(idx));
1282 }
1283 if (scope_info->HasFunctionName()) {
1284 String* name = scope_info->FunctionName();
1285 VariableMode mode;
1286 int idx = scope_info->FunctionContextSlotIndex(name, &mode);
1287 if (idx >= 0) {
1288 SetContextReference(context, entry, name, context->get(idx),
1289 Context::OffsetOfElementAt(idx));
1290 }
1291 }
1292 }
1293
1294 #define EXTRACT_CONTEXT_FIELD(index, type, name) \
1295 if (Context::index < Context::FIRST_WEAK_SLOT || \
1296 Context::index == Context::MAP_CACHE_INDEX) { \
1297 SetInternalReference(context, entry, #name, context->get(Context::index), \
1298 FixedArray::OffsetOfElementAt(Context::index)); \
1299 } else { \
1300 SetWeakReference(context, entry, #name, context->get(Context::index), \
1301 FixedArray::OffsetOfElementAt(Context::index)); \
1302 }
1303 EXTRACT_CONTEXT_FIELD(CLOSURE_INDEX, JSFunction, closure);
1304 EXTRACT_CONTEXT_FIELD(PREVIOUS_INDEX, Context, previous);
1305 EXTRACT_CONTEXT_FIELD(EXTENSION_INDEX, Object, extension);
1306 EXTRACT_CONTEXT_FIELD(GLOBAL_OBJECT_INDEX, GlobalObject, global);
1307 if (context->IsNativeContext()) {
1308 TagObject(context->jsfunction_result_caches(),
1309 "(context func. result caches)");
1310 TagObject(context->normalized_map_cache(), "(context norm. map cache)");
1311 TagObject(context->runtime_context(), "(runtime context)");
1312 TagObject(context->embedder_data(), "(context data)");
1313 NATIVE_CONTEXT_FIELDS(EXTRACT_CONTEXT_FIELD);
1314 EXTRACT_CONTEXT_FIELD(OPTIMIZED_FUNCTIONS_LIST, unused,
1315 optimized_functions_list);
1316 EXTRACT_CONTEXT_FIELD(OPTIMIZED_CODE_LIST, unused, optimized_code_list);
1317 EXTRACT_CONTEXT_FIELD(DEOPTIMIZED_CODE_LIST, unused, deoptimized_code_list);
1318 EXTRACT_CONTEXT_FIELD(NEXT_CONTEXT_LINK, unused, next_context_link);
1319 #undef EXTRACT_CONTEXT_FIELD
1320 STATIC_ASSERT(Context::OPTIMIZED_FUNCTIONS_LIST ==
1321 Context::FIRST_WEAK_SLOT);
1322 STATIC_ASSERT(Context::NEXT_CONTEXT_LINK + 1 ==
1323 Context::NATIVE_CONTEXT_SLOTS);
1324 STATIC_ASSERT(Context::FIRST_WEAK_SLOT + 5 ==
1325 Context::NATIVE_CONTEXT_SLOTS);
1326 }
1327 }
1328
1329
ExtractMapReferences(int entry,Map * map)1330 void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) {
1331 if (map->HasTransitionArray()) {
1332 TransitionArray* transitions = map->transitions();
1333 int transitions_entry = GetEntry(transitions)->index();
1334 Object* back_pointer = transitions->back_pointer_storage();
1335 TagObject(back_pointer, "(back pointer)");
1336 SetInternalReference(transitions, transitions_entry,
1337 "back_pointer", back_pointer);
1338
1339 if (FLAG_collect_maps && map->CanTransition()) {
1340 if (!transitions->IsSimpleTransition()) {
1341 if (transitions->HasPrototypeTransitions()) {
1342 FixedArray* prototype_transitions =
1343 transitions->GetPrototypeTransitions();
1344 MarkAsWeakContainer(prototype_transitions);
1345 TagObject(prototype_transitions, "(prototype transitions");
1346 SetInternalReference(transitions, transitions_entry,
1347 "prototype_transitions", prototype_transitions);
1348 }
1349 // TODO(alph): transitions keys are strong links.
1350 MarkAsWeakContainer(transitions);
1351 }
1352 }
1353
1354 TagObject(transitions, "(transition array)");
1355 SetInternalReference(map, entry,
1356 "transitions", transitions,
1357 Map::kTransitionsOrBackPointerOffset);
1358 } else {
1359 Object* back_pointer = map->GetBackPointer();
1360 TagObject(back_pointer, "(back pointer)");
1361 SetInternalReference(map, entry,
1362 "back_pointer", back_pointer,
1363 Map::kTransitionsOrBackPointerOffset);
1364 }
1365 DescriptorArray* descriptors = map->instance_descriptors();
1366 TagObject(descriptors, "(map descriptors)");
1367 SetInternalReference(map, entry,
1368 "descriptors", descriptors,
1369 Map::kDescriptorsOffset);
1370
1371 MarkAsWeakContainer(map->code_cache());
1372 SetInternalReference(map, entry,
1373 "code_cache", map->code_cache(),
1374 Map::kCodeCacheOffset);
1375 SetInternalReference(map, entry,
1376 "prototype", map->prototype(), Map::kPrototypeOffset);
1377 SetInternalReference(map, entry,
1378 "constructor", map->constructor(),
1379 Map::kConstructorOffset);
1380 TagObject(map->dependent_code(), "(dependent code)");
1381 MarkAsWeakContainer(map->dependent_code());
1382 SetInternalReference(map, entry,
1383 "dependent_code", map->dependent_code(),
1384 Map::kDependentCodeOffset);
1385 }
1386
1387
ExtractSharedFunctionInfoReferences(int entry,SharedFunctionInfo * shared)1388 void V8HeapExplorer::ExtractSharedFunctionInfoReferences(
1389 int entry, SharedFunctionInfo* shared) {
1390 HeapObject* obj = shared;
1391 String* shared_name = shared->DebugName();
1392 const char* name = NULL;
1393 if (shared_name != *heap_->isolate()->factory()->empty_string()) {
1394 name = names_->GetName(shared_name);
1395 TagObject(shared->code(), names_->GetFormatted("(code for %s)", name));
1396 } else {
1397 TagObject(shared->code(), names_->GetFormatted("(%s code)",
1398 Code::Kind2String(shared->code()->kind())));
1399 }
1400
1401 SetInternalReference(obj, entry,
1402 "name", shared->name(),
1403 SharedFunctionInfo::kNameOffset);
1404 SetInternalReference(obj, entry,
1405 "code", shared->code(),
1406 SharedFunctionInfo::kCodeOffset);
1407 TagObject(shared->scope_info(), "(function scope info)");
1408 SetInternalReference(obj, entry,
1409 "scope_info", shared->scope_info(),
1410 SharedFunctionInfo::kScopeInfoOffset);
1411 SetInternalReference(obj, entry,
1412 "instance_class_name", shared->instance_class_name(),
1413 SharedFunctionInfo::kInstanceClassNameOffset);
1414 SetInternalReference(obj, entry,
1415 "script", shared->script(),
1416 SharedFunctionInfo::kScriptOffset);
1417 const char* construct_stub_name = name ?
1418 names_->GetFormatted("(construct stub code for %s)", name) :
1419 "(construct stub code)";
1420 TagObject(shared->construct_stub(), construct_stub_name);
1421 SetInternalReference(obj, entry,
1422 "construct_stub", shared->construct_stub(),
1423 SharedFunctionInfo::kConstructStubOffset);
1424 SetInternalReference(obj, entry,
1425 "function_data", shared->function_data(),
1426 SharedFunctionInfo::kFunctionDataOffset);
1427 SetInternalReference(obj, entry,
1428 "debug_info", shared->debug_info(),
1429 SharedFunctionInfo::kDebugInfoOffset);
1430 SetInternalReference(obj, entry,
1431 "inferred_name", shared->inferred_name(),
1432 SharedFunctionInfo::kInferredNameOffset);
1433 SetInternalReference(obj, entry,
1434 "optimized_code_map", shared->optimized_code_map(),
1435 SharedFunctionInfo::kOptimizedCodeMapOffset);
1436 SetInternalReference(obj, entry,
1437 "feedback_vector", shared->feedback_vector(),
1438 SharedFunctionInfo::kFeedbackVectorOffset);
1439 }
1440
1441
ExtractScriptReferences(int entry,Script * script)1442 void V8HeapExplorer::ExtractScriptReferences(int entry, Script* script) {
1443 HeapObject* obj = script;
1444 SetInternalReference(obj, entry,
1445 "source", script->source(),
1446 Script::kSourceOffset);
1447 SetInternalReference(obj, entry,
1448 "name", script->name(),
1449 Script::kNameOffset);
1450 SetInternalReference(obj, entry,
1451 "context_data", script->context_data(),
1452 Script::kContextOffset);
1453 TagObject(script->line_ends(), "(script line ends)");
1454 SetInternalReference(obj, entry,
1455 "line_ends", script->line_ends(),
1456 Script::kLineEndsOffset);
1457 }
1458
1459
ExtractAccessorPairReferences(int entry,AccessorPair * accessors)1460 void V8HeapExplorer::ExtractAccessorPairReferences(
1461 int entry, AccessorPair* accessors) {
1462 SetInternalReference(accessors, entry, "getter", accessors->getter(),
1463 AccessorPair::kGetterOffset);
1464 SetInternalReference(accessors, entry, "setter", accessors->setter(),
1465 AccessorPair::kSetterOffset);
1466 }
1467
1468
ExtractCodeCacheReferences(int entry,CodeCache * code_cache)1469 void V8HeapExplorer::ExtractCodeCacheReferences(
1470 int entry, CodeCache* code_cache) {
1471 TagObject(code_cache->default_cache(), "(default code cache)");
1472 SetInternalReference(code_cache, entry,
1473 "default_cache", code_cache->default_cache(),
1474 CodeCache::kDefaultCacheOffset);
1475 TagObject(code_cache->normal_type_cache(), "(code type cache)");
1476 SetInternalReference(code_cache, entry,
1477 "type_cache", code_cache->normal_type_cache(),
1478 CodeCache::kNormalTypeCacheOffset);
1479 }
1480
1481
TagBuiltinCodeObject(Code * code,const char * name)1482 void V8HeapExplorer::TagBuiltinCodeObject(Code* code, const char* name) {
1483 TagObject(code, names_->GetFormatted("(%s builtin)", name));
1484 }
1485
1486
TagCodeObject(Code * code)1487 void V8HeapExplorer::TagCodeObject(Code* code) {
1488 if (code->kind() == Code::STUB) {
1489 TagObject(code, names_->GetFormatted(
1490 "(%s code)", CodeStub::MajorName(
1491 static_cast<CodeStub::Major>(code->major_key()), true)));
1492 }
1493 }
1494
1495
ExtractCodeReferences(int entry,Code * code)1496 void V8HeapExplorer::ExtractCodeReferences(int entry, Code* code) {
1497 TagCodeObject(code);
1498 TagObject(code->relocation_info(), "(code relocation info)");
1499 SetInternalReference(code, entry,
1500 "relocation_info", code->relocation_info(),
1501 Code::kRelocationInfoOffset);
1502 SetInternalReference(code, entry,
1503 "handler_table", code->handler_table(),
1504 Code::kHandlerTableOffset);
1505 TagObject(code->deoptimization_data(), "(code deopt data)");
1506 SetInternalReference(code, entry,
1507 "deoptimization_data", code->deoptimization_data(),
1508 Code::kDeoptimizationDataOffset);
1509 if (code->kind() == Code::FUNCTION) {
1510 SetInternalReference(code, entry,
1511 "type_feedback_info", code->type_feedback_info(),
1512 Code::kTypeFeedbackInfoOffset);
1513 }
1514 SetInternalReference(code, entry,
1515 "gc_metadata", code->gc_metadata(),
1516 Code::kGCMetadataOffset);
1517 SetInternalReference(code, entry,
1518 "constant_pool", code->constant_pool(),
1519 Code::kConstantPoolOffset);
1520 if (code->kind() == Code::OPTIMIZED_FUNCTION) {
1521 SetWeakReference(code, entry,
1522 "next_code_link", code->next_code_link(),
1523 Code::kNextCodeLinkOffset);
1524 }
1525 }
1526
1527
ExtractBoxReferences(int entry,Box * box)1528 void V8HeapExplorer::ExtractBoxReferences(int entry, Box* box) {
1529 SetInternalReference(box, entry, "value", box->value(), Box::kValueOffset);
1530 }
1531
1532
ExtractCellReferences(int entry,Cell * cell)1533 void V8HeapExplorer::ExtractCellReferences(int entry, Cell* cell) {
1534 SetInternalReference(cell, entry, "value", cell->value(), Cell::kValueOffset);
1535 }
1536
1537
ExtractPropertyCellReferences(int entry,PropertyCell * cell)1538 void V8HeapExplorer::ExtractPropertyCellReferences(int entry,
1539 PropertyCell* cell) {
1540 ExtractCellReferences(entry, cell);
1541 SetInternalReference(cell, entry, "type", cell->type(),
1542 PropertyCell::kTypeOffset);
1543 MarkAsWeakContainer(cell->dependent_code());
1544 SetInternalReference(cell, entry, "dependent_code", cell->dependent_code(),
1545 PropertyCell::kDependentCodeOffset);
1546 }
1547
1548
ExtractAllocationSiteReferences(int entry,AllocationSite * site)1549 void V8HeapExplorer::ExtractAllocationSiteReferences(int entry,
1550 AllocationSite* site) {
1551 SetInternalReference(site, entry, "transition_info", site->transition_info(),
1552 AllocationSite::kTransitionInfoOffset);
1553 SetInternalReference(site, entry, "nested_site", site->nested_site(),
1554 AllocationSite::kNestedSiteOffset);
1555 MarkAsWeakContainer(site->dependent_code());
1556 SetInternalReference(site, entry, "dependent_code", site->dependent_code(),
1557 AllocationSite::kDependentCodeOffset);
1558 // Do not visit weak_next as it is not visited by the StaticVisitor,
1559 // and we're not very interested in weak_next field here.
1560 STATIC_ASSERT(AllocationSite::kWeakNextOffset >=
1561 AllocationSite::BodyDescriptor::kEndOffset);
1562 }
1563
1564
1565 class JSArrayBufferDataEntryAllocator : public HeapEntriesAllocator {
1566 public:
JSArrayBufferDataEntryAllocator(size_t size,V8HeapExplorer * explorer)1567 JSArrayBufferDataEntryAllocator(size_t size, V8HeapExplorer* explorer)
1568 : size_(size)
1569 , explorer_(explorer) {
1570 }
AllocateEntry(HeapThing ptr)1571 virtual HeapEntry* AllocateEntry(HeapThing ptr) {
1572 return explorer_->AddEntry(
1573 static_cast<Address>(ptr),
1574 HeapEntry::kNative, "system / JSArrayBufferData", size_);
1575 }
1576 private:
1577 size_t size_;
1578 V8HeapExplorer* explorer_;
1579 };
1580
1581
ExtractJSArrayBufferReferences(int entry,JSArrayBuffer * buffer)1582 void V8HeapExplorer::ExtractJSArrayBufferReferences(
1583 int entry, JSArrayBuffer* buffer) {
1584 SetWeakReference(buffer, entry, "weak_next", buffer->weak_next(),
1585 JSArrayBuffer::kWeakNextOffset);
1586 SetWeakReference(buffer, entry,
1587 "weak_first_view", buffer->weak_first_view(),
1588 JSArrayBuffer::kWeakFirstViewOffset);
1589 // Setup a reference to a native memory backing_store object.
1590 if (!buffer->backing_store())
1591 return;
1592 size_t data_size = NumberToSize(heap_->isolate(), buffer->byte_length());
1593 JSArrayBufferDataEntryAllocator allocator(data_size, this);
1594 HeapEntry* data_entry =
1595 filler_->FindOrAddEntry(buffer->backing_store(), &allocator);
1596 filler_->SetNamedReference(HeapGraphEdge::kInternal,
1597 entry, "backing_store", data_entry);
1598 }
1599
1600
ExtractFixedArrayReferences(int entry,FixedArray * array)1601 void V8HeapExplorer::ExtractFixedArrayReferences(int entry, FixedArray* array) {
1602 bool is_weak = weak_containers_.Contains(array);
1603 for (int i = 0, l = array->length(); i < l; ++i) {
1604 if (is_weak) {
1605 SetWeakReference(array, entry,
1606 i, array->get(i), array->OffsetOfElementAt(i));
1607 } else {
1608 SetInternalReference(array, entry,
1609 i, array->get(i), array->OffsetOfElementAt(i));
1610 }
1611 }
1612 }
1613
1614
ExtractClosureReferences(JSObject * js_obj,int entry)1615 void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj, int entry) {
1616 if (!js_obj->IsJSFunction()) return;
1617
1618 JSFunction* func = JSFunction::cast(js_obj);
1619 if (func->shared()->bound()) {
1620 FixedArray* bindings = func->function_bindings();
1621 SetNativeBindReference(js_obj, entry, "bound_this",
1622 bindings->get(JSFunction::kBoundThisIndex));
1623 SetNativeBindReference(js_obj, entry, "bound_function",
1624 bindings->get(JSFunction::kBoundFunctionIndex));
1625 for (int i = JSFunction::kBoundArgumentsStartIndex;
1626 i < bindings->length(); i++) {
1627 const char* reference_name = names_->GetFormatted(
1628 "bound_argument_%d",
1629 i - JSFunction::kBoundArgumentsStartIndex);
1630 SetNativeBindReference(js_obj, entry, reference_name,
1631 bindings->get(i));
1632 }
1633 }
1634 }
1635
1636
ExtractPropertyReferences(JSObject * js_obj,int entry)1637 void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, int entry) {
1638 if (js_obj->HasFastProperties()) {
1639 DescriptorArray* descs = js_obj->map()->instance_descriptors();
1640 int real_size = js_obj->map()->NumberOfOwnDescriptors();
1641 for (int i = 0; i < real_size; i++) {
1642 switch (descs->GetType(i)) {
1643 case FIELD: {
1644 int index = descs->GetFieldIndex(i);
1645
1646 Name* k = descs->GetKey(i);
1647 if (index < js_obj->map()->inobject_properties()) {
1648 Object* value = js_obj->InObjectPropertyAt(index);
1649 if (k != heap_->hidden_string()) {
1650 SetPropertyReference(
1651 js_obj, entry,
1652 k, value,
1653 NULL,
1654 js_obj->GetInObjectPropertyOffset(index));
1655 } else {
1656 TagObject(value, "(hidden properties)");
1657 SetInternalReference(
1658 js_obj, entry,
1659 "hidden_properties", value,
1660 js_obj->GetInObjectPropertyOffset(index));
1661 }
1662 } else {
1663 FieldIndex field_index =
1664 FieldIndex::ForDescriptor(js_obj->map(), i);
1665 Object* value = js_obj->RawFastPropertyAt(field_index);
1666 if (k != heap_->hidden_string()) {
1667 SetPropertyReference(js_obj, entry, k, value);
1668 } else {
1669 TagObject(value, "(hidden properties)");
1670 SetInternalReference(js_obj, entry, "hidden_properties", value);
1671 }
1672 }
1673 break;
1674 }
1675 case CONSTANT:
1676 SetPropertyReference(
1677 js_obj, entry,
1678 descs->GetKey(i), descs->GetConstant(i));
1679 break;
1680 case CALLBACKS:
1681 ExtractAccessorPairProperty(
1682 js_obj, entry,
1683 descs->GetKey(i), descs->GetValue(i));
1684 break;
1685 case NORMAL: // only in slow mode
1686 case HANDLER: // only in lookup results, not in descriptors
1687 case INTERCEPTOR: // only in lookup results, not in descriptors
1688 break;
1689 case NONEXISTENT:
1690 UNREACHABLE();
1691 break;
1692 }
1693 }
1694 } else {
1695 NameDictionary* dictionary = js_obj->property_dictionary();
1696 int length = dictionary->Capacity();
1697 for (int i = 0; i < length; ++i) {
1698 Object* k = dictionary->KeyAt(i);
1699 if (dictionary->IsKey(k)) {
1700 Object* target = dictionary->ValueAt(i);
1701 // We assume that global objects can only have slow properties.
1702 Object* value = target->IsPropertyCell()
1703 ? PropertyCell::cast(target)->value()
1704 : target;
1705 if (k == heap_->hidden_string()) {
1706 TagObject(value, "(hidden properties)");
1707 SetInternalReference(js_obj, entry, "hidden_properties", value);
1708 continue;
1709 }
1710 if (ExtractAccessorPairProperty(js_obj, entry, k, value)) continue;
1711 SetPropertyReference(js_obj, entry, String::cast(k), value);
1712 }
1713 }
1714 }
1715 }
1716
1717
ExtractAccessorPairProperty(JSObject * js_obj,int entry,Object * key,Object * callback_obj)1718 bool V8HeapExplorer::ExtractAccessorPairProperty(
1719 JSObject* js_obj, int entry, Object* key, Object* callback_obj) {
1720 if (!callback_obj->IsAccessorPair()) return false;
1721 AccessorPair* accessors = AccessorPair::cast(callback_obj);
1722 Object* getter = accessors->getter();
1723 if (!getter->IsOddball()) {
1724 SetPropertyReference(js_obj, entry, String::cast(key), getter, "get %s");
1725 }
1726 Object* setter = accessors->setter();
1727 if (!setter->IsOddball()) {
1728 SetPropertyReference(js_obj, entry, String::cast(key), setter, "set %s");
1729 }
1730 return true;
1731 }
1732
1733
ExtractElementReferences(JSObject * js_obj,int entry)1734 void V8HeapExplorer::ExtractElementReferences(JSObject* js_obj, int entry) {
1735 if (js_obj->HasFastObjectElements()) {
1736 FixedArray* elements = FixedArray::cast(js_obj->elements());
1737 int length = js_obj->IsJSArray() ?
1738 Smi::cast(JSArray::cast(js_obj)->length())->value() :
1739 elements->length();
1740 for (int i = 0; i < length; ++i) {
1741 if (!elements->get(i)->IsTheHole()) {
1742 SetElementReference(js_obj, entry, i, elements->get(i));
1743 }
1744 }
1745 } else if (js_obj->HasDictionaryElements()) {
1746 SeededNumberDictionary* dictionary = js_obj->element_dictionary();
1747 int length = dictionary->Capacity();
1748 for (int i = 0; i < length; ++i) {
1749 Object* k = dictionary->KeyAt(i);
1750 if (dictionary->IsKey(k)) {
1751 ASSERT(k->IsNumber());
1752 uint32_t index = static_cast<uint32_t>(k->Number());
1753 SetElementReference(js_obj, entry, index, dictionary->ValueAt(i));
1754 }
1755 }
1756 }
1757 }
1758
1759
ExtractInternalReferences(JSObject * js_obj,int entry)1760 void V8HeapExplorer::ExtractInternalReferences(JSObject* js_obj, int entry) {
1761 int length = js_obj->GetInternalFieldCount();
1762 for (int i = 0; i < length; ++i) {
1763 Object* o = js_obj->GetInternalField(i);
1764 SetInternalReference(
1765 js_obj, entry, i, o, js_obj->GetInternalFieldOffset(i));
1766 }
1767 }
1768
1769
GetConstructorName(JSObject * object)1770 String* V8HeapExplorer::GetConstructorName(JSObject* object) {
1771 Heap* heap = object->GetHeap();
1772 if (object->IsJSFunction()) return heap->closure_string();
1773 String* constructor_name = object->constructor_name();
1774 if (constructor_name == heap->Object_string()) {
1775 // Look up an immediate "constructor" property, if it is a function,
1776 // return its name. This is for instances of binding objects, which
1777 // have prototype constructor type "Object".
1778 Object* constructor_prop = NULL;
1779 Isolate* isolate = heap->isolate();
1780 LookupResult result(isolate);
1781 object->LookupOwnRealNamedProperty(
1782 isolate->factory()->constructor_string(), &result);
1783 if (!result.IsFound()) return object->constructor_name();
1784
1785 constructor_prop = result.GetLazyValue();
1786 if (constructor_prop->IsJSFunction()) {
1787 Object* maybe_name =
1788 JSFunction::cast(constructor_prop)->shared()->name();
1789 if (maybe_name->IsString()) {
1790 String* name = String::cast(maybe_name);
1791 if (name->length() > 0) return name;
1792 }
1793 }
1794 }
1795 return object->constructor_name();
1796 }
1797
1798
GetEntry(Object * obj)1799 HeapEntry* V8HeapExplorer::GetEntry(Object* obj) {
1800 if (!obj->IsHeapObject()) return NULL;
1801 return filler_->FindOrAddEntry(obj, this);
1802 }
1803
1804
1805 class RootsReferencesExtractor : public ObjectVisitor {
1806 private:
1807 struct IndexTag {
IndexTagv8::internal::RootsReferencesExtractor::IndexTag1808 IndexTag(int index, VisitorSynchronization::SyncTag tag)
1809 : index(index), tag(tag) { }
1810 int index;
1811 VisitorSynchronization::SyncTag tag;
1812 };
1813
1814 public:
RootsReferencesExtractor(Heap * heap)1815 explicit RootsReferencesExtractor(Heap* heap)
1816 : collecting_all_references_(false),
1817 previous_reference_count_(0),
1818 heap_(heap) {
1819 }
1820
VisitPointers(Object ** start,Object ** end)1821 void VisitPointers(Object** start, Object** end) {
1822 if (collecting_all_references_) {
1823 for (Object** p = start; p < end; p++) all_references_.Add(*p);
1824 } else {
1825 for (Object** p = start; p < end; p++) strong_references_.Add(*p);
1826 }
1827 }
1828
SetCollectingAllReferences()1829 void SetCollectingAllReferences() { collecting_all_references_ = true; }
1830
FillReferences(V8HeapExplorer * explorer)1831 void FillReferences(V8HeapExplorer* explorer) {
1832 ASSERT(strong_references_.length() <= all_references_.length());
1833 Builtins* builtins = heap_->isolate()->builtins();
1834 for (int i = 0; i < reference_tags_.length(); ++i) {
1835 explorer->SetGcRootsReference(reference_tags_[i].tag);
1836 }
1837 int strong_index = 0, all_index = 0, tags_index = 0, builtin_index = 0;
1838 while (all_index < all_references_.length()) {
1839 bool is_strong = strong_index < strong_references_.length()
1840 && strong_references_[strong_index] == all_references_[all_index];
1841 explorer->SetGcSubrootReference(reference_tags_[tags_index].tag,
1842 !is_strong,
1843 all_references_[all_index]);
1844 if (reference_tags_[tags_index].tag ==
1845 VisitorSynchronization::kBuiltins) {
1846 ASSERT(all_references_[all_index]->IsCode());
1847 explorer->TagBuiltinCodeObject(
1848 Code::cast(all_references_[all_index]),
1849 builtins->name(builtin_index++));
1850 }
1851 ++all_index;
1852 if (is_strong) ++strong_index;
1853 if (reference_tags_[tags_index].index == all_index) ++tags_index;
1854 }
1855 }
1856
Synchronize(VisitorSynchronization::SyncTag tag)1857 void Synchronize(VisitorSynchronization::SyncTag tag) {
1858 if (collecting_all_references_ &&
1859 previous_reference_count_ != all_references_.length()) {
1860 previous_reference_count_ = all_references_.length();
1861 reference_tags_.Add(IndexTag(previous_reference_count_, tag));
1862 }
1863 }
1864
1865 private:
1866 bool collecting_all_references_;
1867 List<Object*> strong_references_;
1868 List<Object*> all_references_;
1869 int previous_reference_count_;
1870 List<IndexTag> reference_tags_;
1871 Heap* heap_;
1872 };
1873
1874
IterateAndExtractReferences(SnapshotFiller * filler)1875 bool V8HeapExplorer::IterateAndExtractReferences(
1876 SnapshotFiller* filler) {
1877 filler_ = filler;
1878
1879 // Make sure builtin code objects get their builtin tags
1880 // first. Otherwise a particular JSFunction object could set
1881 // its custom name to a generic builtin.
1882 SetRootGcRootsReference();
1883 RootsReferencesExtractor extractor(heap_);
1884 heap_->IterateRoots(&extractor, VISIT_ONLY_STRONG);
1885 extractor.SetCollectingAllReferences();
1886 heap_->IterateRoots(&extractor, VISIT_ALL);
1887 extractor.FillReferences(this);
1888
1889 // We have to do two passes as sometimes FixedArrays are used
1890 // to weakly hold their items, and it's impossible to distinguish
1891 // between these cases without processing the array owner first.
1892 bool interrupted =
1893 IterateAndExtractSinglePass<&V8HeapExplorer::ExtractReferencesPass1>() ||
1894 IterateAndExtractSinglePass<&V8HeapExplorer::ExtractReferencesPass2>();
1895
1896 if (interrupted) {
1897 filler_ = NULL;
1898 return false;
1899 }
1900
1901 filler_ = NULL;
1902 return progress_->ProgressReport(true);
1903 }
1904
1905
1906 template<V8HeapExplorer::ExtractReferencesMethod extractor>
IterateAndExtractSinglePass()1907 bool V8HeapExplorer::IterateAndExtractSinglePass() {
1908 // Now iterate the whole heap.
1909 bool interrupted = false;
1910 HeapIterator iterator(heap_, HeapIterator::kFilterUnreachable);
1911 // Heap iteration with filtering must be finished in any case.
1912 for (HeapObject* obj = iterator.next();
1913 obj != NULL;
1914 obj = iterator.next(), progress_->ProgressStep()) {
1915 if (interrupted) continue;
1916
1917 HeapEntry* heap_entry = GetEntry(obj);
1918 int entry = heap_entry->index();
1919 if ((this->*extractor)(entry, obj)) {
1920 SetInternalReference(obj, entry,
1921 "map", obj->map(), HeapObject::kMapOffset);
1922 // Extract unvisited fields as hidden references and restore tags
1923 // of visited fields.
1924 IndexedReferencesExtractor refs_extractor(this, obj, entry);
1925 obj->Iterate(&refs_extractor);
1926 }
1927
1928 if (!progress_->ProgressReport(false)) interrupted = true;
1929 }
1930 return interrupted;
1931 }
1932
1933
IsEssentialObject(Object * object)1934 bool V8HeapExplorer::IsEssentialObject(Object* object) {
1935 return object->IsHeapObject()
1936 && !object->IsOddball()
1937 && object != heap_->empty_byte_array()
1938 && object != heap_->empty_fixed_array()
1939 && object != heap_->empty_descriptor_array()
1940 && object != heap_->fixed_array_map()
1941 && object != heap_->cell_map()
1942 && object != heap_->global_property_cell_map()
1943 && object != heap_->shared_function_info_map()
1944 && object != heap_->free_space_map()
1945 && object != heap_->one_pointer_filler_map()
1946 && object != heap_->two_pointer_filler_map();
1947 }
1948
1949
SetContextReference(HeapObject * parent_obj,int parent_entry,String * reference_name,Object * child_obj,int field_offset)1950 void V8HeapExplorer::SetContextReference(HeapObject* parent_obj,
1951 int parent_entry,
1952 String* reference_name,
1953 Object* child_obj,
1954 int field_offset) {
1955 ASSERT(parent_entry == GetEntry(parent_obj)->index());
1956 HeapEntry* child_entry = GetEntry(child_obj);
1957 if (child_entry != NULL) {
1958 filler_->SetNamedReference(HeapGraphEdge::kContextVariable,
1959 parent_entry,
1960 names_->GetName(reference_name),
1961 child_entry);
1962 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
1963 }
1964 }
1965
1966
SetNativeBindReference(HeapObject * parent_obj,int parent_entry,const char * reference_name,Object * child_obj)1967 void V8HeapExplorer::SetNativeBindReference(HeapObject* parent_obj,
1968 int parent_entry,
1969 const char* reference_name,
1970 Object* child_obj) {
1971 ASSERT(parent_entry == GetEntry(parent_obj)->index());
1972 HeapEntry* child_entry = GetEntry(child_obj);
1973 if (child_entry != NULL) {
1974 filler_->SetNamedReference(HeapGraphEdge::kShortcut,
1975 parent_entry,
1976 reference_name,
1977 child_entry);
1978 }
1979 }
1980
1981
SetElementReference(HeapObject * parent_obj,int parent_entry,int index,Object * child_obj)1982 void V8HeapExplorer::SetElementReference(HeapObject* parent_obj,
1983 int parent_entry,
1984 int index,
1985 Object* child_obj) {
1986 ASSERT(parent_entry == GetEntry(parent_obj)->index());
1987 HeapEntry* child_entry = GetEntry(child_obj);
1988 if (child_entry != NULL) {
1989 filler_->SetIndexedReference(HeapGraphEdge::kElement,
1990 parent_entry,
1991 index,
1992 child_entry);
1993 }
1994 }
1995
1996
SetInternalReference(HeapObject * parent_obj,int parent_entry,const char * reference_name,Object * child_obj,int field_offset)1997 void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
1998 int parent_entry,
1999 const char* reference_name,
2000 Object* child_obj,
2001 int field_offset) {
2002 ASSERT(parent_entry == GetEntry(parent_obj)->index());
2003 HeapEntry* child_entry = GetEntry(child_obj);
2004 if (child_entry == NULL) return;
2005 if (IsEssentialObject(child_obj)) {
2006 filler_->SetNamedReference(HeapGraphEdge::kInternal,
2007 parent_entry,
2008 reference_name,
2009 child_entry);
2010 }
2011 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
2012 }
2013
2014
SetInternalReference(HeapObject * parent_obj,int parent_entry,int index,Object * child_obj,int field_offset)2015 void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
2016 int parent_entry,
2017 int index,
2018 Object* child_obj,
2019 int field_offset) {
2020 ASSERT(parent_entry == GetEntry(parent_obj)->index());
2021 HeapEntry* child_entry = GetEntry(child_obj);
2022 if (child_entry == NULL) return;
2023 if (IsEssentialObject(child_obj)) {
2024 filler_->SetNamedReference(HeapGraphEdge::kInternal,
2025 parent_entry,
2026 names_->GetName(index),
2027 child_entry);
2028 }
2029 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
2030 }
2031
2032
SetHiddenReference(HeapObject * parent_obj,int parent_entry,int index,Object * child_obj)2033 void V8HeapExplorer::SetHiddenReference(HeapObject* parent_obj,
2034 int parent_entry,
2035 int index,
2036 Object* child_obj) {
2037 ASSERT(parent_entry == GetEntry(parent_obj)->index());
2038 HeapEntry* child_entry = GetEntry(child_obj);
2039 if (child_entry != NULL && IsEssentialObject(child_obj)) {
2040 filler_->SetIndexedReference(HeapGraphEdge::kHidden,
2041 parent_entry,
2042 index,
2043 child_entry);
2044 }
2045 }
2046
2047
SetWeakReference(HeapObject * parent_obj,int parent_entry,const char * reference_name,Object * child_obj,int field_offset)2048 void V8HeapExplorer::SetWeakReference(HeapObject* parent_obj,
2049 int parent_entry,
2050 const char* reference_name,
2051 Object* child_obj,
2052 int field_offset) {
2053 ASSERT(parent_entry == GetEntry(parent_obj)->index());
2054 HeapEntry* child_entry = GetEntry(child_obj);
2055 if (child_entry == NULL) return;
2056 if (IsEssentialObject(child_obj)) {
2057 filler_->SetNamedReference(HeapGraphEdge::kWeak,
2058 parent_entry,
2059 reference_name,
2060 child_entry);
2061 }
2062 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
2063 }
2064
2065
SetWeakReference(HeapObject * parent_obj,int parent_entry,int index,Object * child_obj,int field_offset)2066 void V8HeapExplorer::SetWeakReference(HeapObject* parent_obj,
2067 int parent_entry,
2068 int index,
2069 Object* child_obj,
2070 int field_offset) {
2071 ASSERT(parent_entry == GetEntry(parent_obj)->index());
2072 HeapEntry* child_entry = GetEntry(child_obj);
2073 if (child_entry == NULL) return;
2074 if (IsEssentialObject(child_obj)) {
2075 filler_->SetNamedReference(HeapGraphEdge::kWeak,
2076 parent_entry,
2077 names_->GetFormatted("%d", index),
2078 child_entry);
2079 }
2080 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
2081 }
2082
2083
SetPropertyReference(HeapObject * parent_obj,int parent_entry,Name * reference_name,Object * child_obj,const char * name_format_string,int field_offset)2084 void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj,
2085 int parent_entry,
2086 Name* reference_name,
2087 Object* child_obj,
2088 const char* name_format_string,
2089 int field_offset) {
2090 ASSERT(parent_entry == GetEntry(parent_obj)->index());
2091 HeapEntry* child_entry = GetEntry(child_obj);
2092 if (child_entry != NULL) {
2093 HeapGraphEdge::Type type =
2094 reference_name->IsSymbol() || String::cast(reference_name)->length() > 0
2095 ? HeapGraphEdge::kProperty : HeapGraphEdge::kInternal;
2096 const char* name = name_format_string != NULL && reference_name->IsString()
2097 ? names_->GetFormatted(
2098 name_format_string,
2099 String::cast(reference_name)->ToCString(
2100 DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).get()) :
2101 names_->GetName(reference_name);
2102
2103 filler_->SetNamedReference(type,
2104 parent_entry,
2105 name,
2106 child_entry);
2107 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
2108 }
2109 }
2110
2111
SetRootGcRootsReference()2112 void V8HeapExplorer::SetRootGcRootsReference() {
2113 filler_->SetIndexedAutoIndexReference(
2114 HeapGraphEdge::kElement,
2115 snapshot_->root()->index(),
2116 snapshot_->gc_roots());
2117 }
2118
2119
SetUserGlobalReference(Object * child_obj)2120 void V8HeapExplorer::SetUserGlobalReference(Object* child_obj) {
2121 HeapEntry* child_entry = GetEntry(child_obj);
2122 ASSERT(child_entry != NULL);
2123 filler_->SetNamedAutoIndexReference(
2124 HeapGraphEdge::kShortcut,
2125 snapshot_->root()->index(),
2126 child_entry);
2127 }
2128
2129
SetGcRootsReference(VisitorSynchronization::SyncTag tag)2130 void V8HeapExplorer::SetGcRootsReference(VisitorSynchronization::SyncTag tag) {
2131 filler_->SetIndexedAutoIndexReference(
2132 HeapGraphEdge::kElement,
2133 snapshot_->gc_roots()->index(),
2134 snapshot_->gc_subroot(tag));
2135 }
2136
2137
SetGcSubrootReference(VisitorSynchronization::SyncTag tag,bool is_weak,Object * child_obj)2138 void V8HeapExplorer::SetGcSubrootReference(
2139 VisitorSynchronization::SyncTag tag, bool is_weak, Object* child_obj) {
2140 HeapEntry* child_entry = GetEntry(child_obj);
2141 if (child_entry != NULL) {
2142 const char* name = GetStrongGcSubrootName(child_obj);
2143 if (name != NULL) {
2144 filler_->SetNamedReference(
2145 HeapGraphEdge::kInternal,
2146 snapshot_->gc_subroot(tag)->index(),
2147 name,
2148 child_entry);
2149 } else {
2150 if (is_weak) {
2151 filler_->SetNamedAutoIndexReference(
2152 HeapGraphEdge::kWeak,
2153 snapshot_->gc_subroot(tag)->index(),
2154 child_entry);
2155 } else {
2156 filler_->SetIndexedAutoIndexReference(
2157 HeapGraphEdge::kElement,
2158 snapshot_->gc_subroot(tag)->index(),
2159 child_entry);
2160 }
2161 }
2162
2163 // Add a shortcut to JS global object reference at snapshot root.
2164 if (child_obj->IsNativeContext()) {
2165 Context* context = Context::cast(child_obj);
2166 GlobalObject* global = context->global_object();
2167 if (global->IsJSGlobalObject()) {
2168 bool is_debug_object = false;
2169 is_debug_object = heap_->isolate()->debug()->IsDebugGlobal(global);
2170 if (!is_debug_object && !user_roots_.Contains(global)) {
2171 user_roots_.Insert(global);
2172 SetUserGlobalReference(global);
2173 }
2174 }
2175 }
2176 }
2177 }
2178
2179
GetStrongGcSubrootName(Object * object)2180 const char* V8HeapExplorer::GetStrongGcSubrootName(Object* object) {
2181 if (strong_gc_subroot_names_.is_empty()) {
2182 #define NAME_ENTRY(name) strong_gc_subroot_names_.SetTag(heap_->name(), #name);
2183 #define ROOT_NAME(type, name, camel_name) NAME_ENTRY(name)
2184 STRONG_ROOT_LIST(ROOT_NAME)
2185 #undef ROOT_NAME
2186 #define STRUCT_MAP_NAME(NAME, Name, name) NAME_ENTRY(name##_map)
2187 STRUCT_LIST(STRUCT_MAP_NAME)
2188 #undef STRUCT_MAP_NAME
2189 #define STRING_NAME(name, str) NAME_ENTRY(name)
2190 INTERNALIZED_STRING_LIST(STRING_NAME)
2191 #undef STRING_NAME
2192 #undef NAME_ENTRY
2193 CHECK(!strong_gc_subroot_names_.is_empty());
2194 }
2195 return strong_gc_subroot_names_.GetTag(object);
2196 }
2197
2198
TagObject(Object * obj,const char * tag)2199 void V8HeapExplorer::TagObject(Object* obj, const char* tag) {
2200 if (IsEssentialObject(obj)) {
2201 HeapEntry* entry = GetEntry(obj);
2202 if (entry->name()[0] == '\0') {
2203 entry->set_name(tag);
2204 }
2205 }
2206 }
2207
2208
MarkAsWeakContainer(Object * object)2209 void V8HeapExplorer::MarkAsWeakContainer(Object* object) {
2210 if (IsEssentialObject(object) && object->IsFixedArray()) {
2211 weak_containers_.Insert(object);
2212 }
2213 }
2214
2215
2216 class GlobalObjectsEnumerator : public ObjectVisitor {
2217 public:
VisitPointers(Object ** start,Object ** end)2218 virtual void VisitPointers(Object** start, Object** end) {
2219 for (Object** p = start; p < end; p++) {
2220 if ((*p)->IsNativeContext()) {
2221 Context* context = Context::cast(*p);
2222 JSObject* proxy = context->global_proxy();
2223 if (proxy->IsJSGlobalProxy()) {
2224 Object* global = proxy->map()->prototype();
2225 if (global->IsJSGlobalObject()) {
2226 objects_.Add(Handle<JSGlobalObject>(JSGlobalObject::cast(global)));
2227 }
2228 }
2229 }
2230 }
2231 }
count()2232 int count() { return objects_.length(); }
at(int i)2233 Handle<JSGlobalObject>& at(int i) { return objects_[i]; }
2234
2235 private:
2236 List<Handle<JSGlobalObject> > objects_;
2237 };
2238
2239
2240 // Modifies heap. Must not be run during heap traversal.
TagGlobalObjects()2241 void V8HeapExplorer::TagGlobalObjects() {
2242 Isolate* isolate = heap_->isolate();
2243 HandleScope scope(isolate);
2244 GlobalObjectsEnumerator enumerator;
2245 isolate->global_handles()->IterateAllRoots(&enumerator);
2246 const char** urls = NewArray<const char*>(enumerator.count());
2247 for (int i = 0, l = enumerator.count(); i < l; ++i) {
2248 if (global_object_name_resolver_) {
2249 HandleScope scope(isolate);
2250 Handle<JSGlobalObject> global_obj = enumerator.at(i);
2251 urls[i] = global_object_name_resolver_->GetName(
2252 Utils::ToLocal(Handle<JSObject>::cast(global_obj)));
2253 } else {
2254 urls[i] = NULL;
2255 }
2256 }
2257
2258 DisallowHeapAllocation no_allocation;
2259 for (int i = 0, l = enumerator.count(); i < l; ++i) {
2260 objects_tags_.SetTag(*enumerator.at(i), urls[i]);
2261 }
2262
2263 DeleteArray(urls);
2264 }
2265
2266
2267 class GlobalHandlesExtractor : public ObjectVisitor {
2268 public:
GlobalHandlesExtractor(NativeObjectsExplorer * explorer)2269 explicit GlobalHandlesExtractor(NativeObjectsExplorer* explorer)
2270 : explorer_(explorer) {}
~GlobalHandlesExtractor()2271 virtual ~GlobalHandlesExtractor() {}
VisitPointers(Object ** start,Object ** end)2272 virtual void VisitPointers(Object** start, Object** end) {
2273 UNREACHABLE();
2274 }
VisitEmbedderReference(Object ** p,uint16_t class_id)2275 virtual void VisitEmbedderReference(Object** p, uint16_t class_id) {
2276 explorer_->VisitSubtreeWrapper(p, class_id);
2277 }
2278 private:
2279 NativeObjectsExplorer* explorer_;
2280 };
2281
2282
2283 class BasicHeapEntriesAllocator : public HeapEntriesAllocator {
2284 public:
BasicHeapEntriesAllocator(HeapSnapshot * snapshot,HeapEntry::Type entries_type)2285 BasicHeapEntriesAllocator(
2286 HeapSnapshot* snapshot,
2287 HeapEntry::Type entries_type)
2288 : snapshot_(snapshot),
2289 names_(snapshot_->profiler()->names()),
2290 heap_object_map_(snapshot_->profiler()->heap_object_map()),
2291 entries_type_(entries_type) {
2292 }
2293 virtual HeapEntry* AllocateEntry(HeapThing ptr);
2294 private:
2295 HeapSnapshot* snapshot_;
2296 StringsStorage* names_;
2297 HeapObjectsMap* heap_object_map_;
2298 HeapEntry::Type entries_type_;
2299 };
2300
2301
AllocateEntry(HeapThing ptr)2302 HeapEntry* BasicHeapEntriesAllocator::AllocateEntry(HeapThing ptr) {
2303 v8::RetainedObjectInfo* info = reinterpret_cast<v8::RetainedObjectInfo*>(ptr);
2304 intptr_t elements = info->GetElementCount();
2305 intptr_t size = info->GetSizeInBytes();
2306 const char* name = elements != -1
2307 ? names_->GetFormatted(
2308 "%s / %" V8_PTR_PREFIX "d entries", info->GetLabel(), elements)
2309 : names_->GetCopy(info->GetLabel());
2310 return snapshot_->AddEntry(
2311 entries_type_,
2312 name,
2313 heap_object_map_->GenerateId(info),
2314 size != -1 ? static_cast<int>(size) : 0,
2315 0);
2316 }
2317
2318
NativeObjectsExplorer(HeapSnapshot * snapshot,SnapshottingProgressReportingInterface * progress)2319 NativeObjectsExplorer::NativeObjectsExplorer(
2320 HeapSnapshot* snapshot,
2321 SnapshottingProgressReportingInterface* progress)
2322 : isolate_(snapshot->profiler()->heap_object_map()->heap()->isolate()),
2323 snapshot_(snapshot),
2324 names_(snapshot_->profiler()->names()),
2325 progress_(progress),
2326 embedder_queried_(false),
2327 objects_by_info_(RetainedInfosMatch),
2328 native_groups_(StringsMatch),
2329 filler_(NULL) {
2330 synthetic_entries_allocator_ =
2331 new BasicHeapEntriesAllocator(snapshot, HeapEntry::kSynthetic);
2332 native_entries_allocator_ =
2333 new BasicHeapEntriesAllocator(snapshot, HeapEntry::kNative);
2334 }
2335
2336
~NativeObjectsExplorer()2337 NativeObjectsExplorer::~NativeObjectsExplorer() {
2338 for (HashMap::Entry* p = objects_by_info_.Start();
2339 p != NULL;
2340 p = objects_by_info_.Next(p)) {
2341 v8::RetainedObjectInfo* info =
2342 reinterpret_cast<v8::RetainedObjectInfo*>(p->key);
2343 info->Dispose();
2344 List<HeapObject*>* objects =
2345 reinterpret_cast<List<HeapObject*>* >(p->value);
2346 delete objects;
2347 }
2348 for (HashMap::Entry* p = native_groups_.Start();
2349 p != NULL;
2350 p = native_groups_.Next(p)) {
2351 v8::RetainedObjectInfo* info =
2352 reinterpret_cast<v8::RetainedObjectInfo*>(p->value);
2353 info->Dispose();
2354 }
2355 delete synthetic_entries_allocator_;
2356 delete native_entries_allocator_;
2357 }
2358
2359
EstimateObjectsCount()2360 int NativeObjectsExplorer::EstimateObjectsCount() {
2361 FillRetainedObjects();
2362 return objects_by_info_.occupancy();
2363 }
2364
2365
FillRetainedObjects()2366 void NativeObjectsExplorer::FillRetainedObjects() {
2367 if (embedder_queried_) return;
2368 Isolate* isolate = isolate_;
2369 const GCType major_gc_type = kGCTypeMarkSweepCompact;
2370 // Record objects that are joined into ObjectGroups.
2371 isolate->heap()->CallGCPrologueCallbacks(
2372 major_gc_type, kGCCallbackFlagConstructRetainedObjectInfos);
2373 List<ObjectGroup*>* groups = isolate->global_handles()->object_groups();
2374 for (int i = 0; i < groups->length(); ++i) {
2375 ObjectGroup* group = groups->at(i);
2376 if (group->info == NULL) continue;
2377 List<HeapObject*>* list = GetListMaybeDisposeInfo(group->info);
2378 for (size_t j = 0; j < group->length; ++j) {
2379 HeapObject* obj = HeapObject::cast(*group->objects[j]);
2380 list->Add(obj);
2381 in_groups_.Insert(obj);
2382 }
2383 group->info = NULL; // Acquire info object ownership.
2384 }
2385 isolate->global_handles()->RemoveObjectGroups();
2386 isolate->heap()->CallGCEpilogueCallbacks(major_gc_type, kNoGCCallbackFlags);
2387 // Record objects that are not in ObjectGroups, but have class ID.
2388 GlobalHandlesExtractor extractor(this);
2389 isolate->global_handles()->IterateAllRootsWithClassIds(&extractor);
2390 embedder_queried_ = true;
2391 }
2392
2393
FillImplicitReferences()2394 void NativeObjectsExplorer::FillImplicitReferences() {
2395 Isolate* isolate = isolate_;
2396 List<ImplicitRefGroup*>* groups =
2397 isolate->global_handles()->implicit_ref_groups();
2398 for (int i = 0; i < groups->length(); ++i) {
2399 ImplicitRefGroup* group = groups->at(i);
2400 HeapObject* parent = *group->parent;
2401 int parent_entry =
2402 filler_->FindOrAddEntry(parent, native_entries_allocator_)->index();
2403 ASSERT(parent_entry != HeapEntry::kNoEntry);
2404 Object*** children = group->children;
2405 for (size_t j = 0; j < group->length; ++j) {
2406 Object* child = *children[j];
2407 HeapEntry* child_entry =
2408 filler_->FindOrAddEntry(child, native_entries_allocator_);
2409 filler_->SetNamedReference(
2410 HeapGraphEdge::kInternal,
2411 parent_entry,
2412 "native",
2413 child_entry);
2414 }
2415 }
2416 isolate->global_handles()->RemoveImplicitRefGroups();
2417 }
2418
GetListMaybeDisposeInfo(v8::RetainedObjectInfo * info)2419 List<HeapObject*>* NativeObjectsExplorer::GetListMaybeDisposeInfo(
2420 v8::RetainedObjectInfo* info) {
2421 HashMap::Entry* entry =
2422 objects_by_info_.Lookup(info, InfoHash(info), true);
2423 if (entry->value != NULL) {
2424 info->Dispose();
2425 } else {
2426 entry->value = new List<HeapObject*>(4);
2427 }
2428 return reinterpret_cast<List<HeapObject*>* >(entry->value);
2429 }
2430
2431
IterateAndExtractReferences(SnapshotFiller * filler)2432 bool NativeObjectsExplorer::IterateAndExtractReferences(
2433 SnapshotFiller* filler) {
2434 filler_ = filler;
2435 FillRetainedObjects();
2436 FillImplicitReferences();
2437 if (EstimateObjectsCount() > 0) {
2438 for (HashMap::Entry* p = objects_by_info_.Start();
2439 p != NULL;
2440 p = objects_by_info_.Next(p)) {
2441 v8::RetainedObjectInfo* info =
2442 reinterpret_cast<v8::RetainedObjectInfo*>(p->key);
2443 SetNativeRootReference(info);
2444 List<HeapObject*>* objects =
2445 reinterpret_cast<List<HeapObject*>* >(p->value);
2446 for (int i = 0; i < objects->length(); ++i) {
2447 SetWrapperNativeReferences(objects->at(i), info);
2448 }
2449 }
2450 SetRootNativeRootsReference();
2451 }
2452 filler_ = NULL;
2453 return true;
2454 }
2455
2456
2457 class NativeGroupRetainedObjectInfo : public v8::RetainedObjectInfo {
2458 public:
NativeGroupRetainedObjectInfo(const char * label)2459 explicit NativeGroupRetainedObjectInfo(const char* label)
2460 : disposed_(false),
2461 hash_(reinterpret_cast<intptr_t>(label)),
2462 label_(label) {
2463 }
2464
~NativeGroupRetainedObjectInfo()2465 virtual ~NativeGroupRetainedObjectInfo() {}
Dispose()2466 virtual void Dispose() {
2467 CHECK(!disposed_);
2468 disposed_ = true;
2469 delete this;
2470 }
IsEquivalent(RetainedObjectInfo * other)2471 virtual bool IsEquivalent(RetainedObjectInfo* other) {
2472 return hash_ == other->GetHash() && !strcmp(label_, other->GetLabel());
2473 }
GetHash()2474 virtual intptr_t GetHash() { return hash_; }
GetLabel()2475 virtual const char* GetLabel() { return label_; }
2476
2477 private:
2478 bool disposed_;
2479 intptr_t hash_;
2480 const char* label_;
2481 };
2482
2483
FindOrAddGroupInfo(const char * label)2484 NativeGroupRetainedObjectInfo* NativeObjectsExplorer::FindOrAddGroupInfo(
2485 const char* label) {
2486 const char* label_copy = names_->GetCopy(label);
2487 uint32_t hash = StringHasher::HashSequentialString(
2488 label_copy,
2489 static_cast<int>(strlen(label_copy)),
2490 isolate_->heap()->HashSeed());
2491 HashMap::Entry* entry = native_groups_.Lookup(const_cast<char*>(label_copy),
2492 hash, true);
2493 if (entry->value == NULL) {
2494 entry->value = new NativeGroupRetainedObjectInfo(label);
2495 }
2496 return static_cast<NativeGroupRetainedObjectInfo*>(entry->value);
2497 }
2498
2499
SetNativeRootReference(v8::RetainedObjectInfo * info)2500 void NativeObjectsExplorer::SetNativeRootReference(
2501 v8::RetainedObjectInfo* info) {
2502 HeapEntry* child_entry =
2503 filler_->FindOrAddEntry(info, native_entries_allocator_);
2504 ASSERT(child_entry != NULL);
2505 NativeGroupRetainedObjectInfo* group_info =
2506 FindOrAddGroupInfo(info->GetGroupLabel());
2507 HeapEntry* group_entry =
2508 filler_->FindOrAddEntry(group_info, synthetic_entries_allocator_);
2509 filler_->SetNamedAutoIndexReference(
2510 HeapGraphEdge::kInternal,
2511 group_entry->index(),
2512 child_entry);
2513 }
2514
2515
SetWrapperNativeReferences(HeapObject * wrapper,v8::RetainedObjectInfo * info)2516 void NativeObjectsExplorer::SetWrapperNativeReferences(
2517 HeapObject* wrapper, v8::RetainedObjectInfo* info) {
2518 HeapEntry* wrapper_entry = filler_->FindEntry(wrapper);
2519 ASSERT(wrapper_entry != NULL);
2520 HeapEntry* info_entry =
2521 filler_->FindOrAddEntry(info, native_entries_allocator_);
2522 ASSERT(info_entry != NULL);
2523 filler_->SetNamedReference(HeapGraphEdge::kInternal,
2524 wrapper_entry->index(),
2525 "native",
2526 info_entry);
2527 filler_->SetIndexedAutoIndexReference(HeapGraphEdge::kElement,
2528 info_entry->index(),
2529 wrapper_entry);
2530 }
2531
2532
SetRootNativeRootsReference()2533 void NativeObjectsExplorer::SetRootNativeRootsReference() {
2534 for (HashMap::Entry* entry = native_groups_.Start();
2535 entry;
2536 entry = native_groups_.Next(entry)) {
2537 NativeGroupRetainedObjectInfo* group_info =
2538 static_cast<NativeGroupRetainedObjectInfo*>(entry->value);
2539 HeapEntry* group_entry =
2540 filler_->FindOrAddEntry(group_info, native_entries_allocator_);
2541 ASSERT(group_entry != NULL);
2542 filler_->SetIndexedAutoIndexReference(
2543 HeapGraphEdge::kElement,
2544 snapshot_->root()->index(),
2545 group_entry);
2546 }
2547 }
2548
2549
VisitSubtreeWrapper(Object ** p,uint16_t class_id)2550 void NativeObjectsExplorer::VisitSubtreeWrapper(Object** p, uint16_t class_id) {
2551 if (in_groups_.Contains(*p)) return;
2552 Isolate* isolate = isolate_;
2553 v8::RetainedObjectInfo* info =
2554 isolate->heap_profiler()->ExecuteWrapperClassCallback(class_id, p);
2555 if (info == NULL) return;
2556 GetListMaybeDisposeInfo(info)->Add(HeapObject::cast(*p));
2557 }
2558
2559
HeapSnapshotGenerator(HeapSnapshot * snapshot,v8::ActivityControl * control,v8::HeapProfiler::ObjectNameResolver * resolver,Heap * heap)2560 HeapSnapshotGenerator::HeapSnapshotGenerator(
2561 HeapSnapshot* snapshot,
2562 v8::ActivityControl* control,
2563 v8::HeapProfiler::ObjectNameResolver* resolver,
2564 Heap* heap)
2565 : snapshot_(snapshot),
2566 control_(control),
2567 v8_heap_explorer_(snapshot_, this, resolver),
2568 dom_explorer_(snapshot_, this),
2569 heap_(heap) {
2570 }
2571
2572
GenerateSnapshot()2573 bool HeapSnapshotGenerator::GenerateSnapshot() {
2574 v8_heap_explorer_.TagGlobalObjects();
2575
2576 // TODO(1562) Profiler assumes that any object that is in the heap after
2577 // full GC is reachable from the root when computing dominators.
2578 // This is not true for weakly reachable objects.
2579 // As a temporary solution we call GC twice.
2580 heap_->CollectAllGarbage(
2581 Heap::kMakeHeapIterableMask,
2582 "HeapSnapshotGenerator::GenerateSnapshot");
2583 heap_->CollectAllGarbage(
2584 Heap::kMakeHeapIterableMask,
2585 "HeapSnapshotGenerator::GenerateSnapshot");
2586
2587 #ifdef VERIFY_HEAP
2588 Heap* debug_heap = heap_;
2589 CHECK(!debug_heap->old_data_space()->was_swept_conservatively());
2590 CHECK(!debug_heap->old_pointer_space()->was_swept_conservatively());
2591 CHECK(!debug_heap->code_space()->was_swept_conservatively());
2592 CHECK(!debug_heap->cell_space()->was_swept_conservatively());
2593 CHECK(!debug_heap->property_cell_space()->
2594 was_swept_conservatively());
2595 CHECK(!debug_heap->map_space()->was_swept_conservatively());
2596 #endif
2597
2598 #ifdef VERIFY_HEAP
2599 debug_heap->Verify();
2600 #endif
2601
2602 SetProgressTotal(2); // 2 passes.
2603
2604 #ifdef VERIFY_HEAP
2605 debug_heap->Verify();
2606 #endif
2607
2608 if (!FillReferences()) return false;
2609
2610 snapshot_->FillChildren();
2611 snapshot_->RememberLastJSObjectId();
2612
2613 progress_counter_ = progress_total_;
2614 if (!ProgressReport(true)) return false;
2615 return true;
2616 }
2617
2618
ProgressStep()2619 void HeapSnapshotGenerator::ProgressStep() {
2620 ++progress_counter_;
2621 }
2622
2623
ProgressReport(bool force)2624 bool HeapSnapshotGenerator::ProgressReport(bool force) {
2625 const int kProgressReportGranularity = 10000;
2626 if (control_ != NULL
2627 && (force || progress_counter_ % kProgressReportGranularity == 0)) {
2628 return
2629 control_->ReportProgressValue(progress_counter_, progress_total_) ==
2630 v8::ActivityControl::kContinue;
2631 }
2632 return true;
2633 }
2634
2635
SetProgressTotal(int iterations_count)2636 void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) {
2637 if (control_ == NULL) return;
2638 HeapIterator iterator(heap_, HeapIterator::kFilterUnreachable);
2639 progress_total_ = iterations_count * (
2640 v8_heap_explorer_.EstimateObjectsCount(&iterator) +
2641 dom_explorer_.EstimateObjectsCount());
2642 progress_counter_ = 0;
2643 }
2644
2645
FillReferences()2646 bool HeapSnapshotGenerator::FillReferences() {
2647 SnapshotFiller filler(snapshot_, &entries_);
2648 v8_heap_explorer_.AddRootEntries(&filler);
2649 return v8_heap_explorer_.IterateAndExtractReferences(&filler)
2650 && dom_explorer_.IterateAndExtractReferences(&filler);
2651 }
2652
2653
2654 template<int bytes> struct MaxDecimalDigitsIn;
2655 template<> struct MaxDecimalDigitsIn<4> {
2656 static const int kSigned = 11;
2657 static const int kUnsigned = 10;
2658 };
2659 template<> struct MaxDecimalDigitsIn<8> {
2660 static const int kSigned = 20;
2661 static const int kUnsigned = 20;
2662 };
2663
2664
2665 class OutputStreamWriter {
2666 public:
OutputStreamWriter(v8::OutputStream * stream)2667 explicit OutputStreamWriter(v8::OutputStream* stream)
2668 : stream_(stream),
2669 chunk_size_(stream->GetChunkSize()),
2670 chunk_(chunk_size_),
2671 chunk_pos_(0),
2672 aborted_(false) {
2673 ASSERT(chunk_size_ > 0);
2674 }
aborted()2675 bool aborted() { return aborted_; }
AddCharacter(char c)2676 void AddCharacter(char c) {
2677 ASSERT(c != '\0');
2678 ASSERT(chunk_pos_ < chunk_size_);
2679 chunk_[chunk_pos_++] = c;
2680 MaybeWriteChunk();
2681 }
AddString(const char * s)2682 void AddString(const char* s) {
2683 AddSubstring(s, StrLength(s));
2684 }
AddSubstring(const char * s,int n)2685 void AddSubstring(const char* s, int n) {
2686 if (n <= 0) return;
2687 ASSERT(static_cast<size_t>(n) <= strlen(s));
2688 const char* s_end = s + n;
2689 while (s < s_end) {
2690 int s_chunk_size =
2691 Min(chunk_size_ - chunk_pos_, static_cast<int>(s_end - s));
2692 ASSERT(s_chunk_size > 0);
2693 MemCopy(chunk_.start() + chunk_pos_, s, s_chunk_size);
2694 s += s_chunk_size;
2695 chunk_pos_ += s_chunk_size;
2696 MaybeWriteChunk();
2697 }
2698 }
AddNumber(unsigned n)2699 void AddNumber(unsigned n) { AddNumberImpl<unsigned>(n, "%u"); }
Finalize()2700 void Finalize() {
2701 if (aborted_) return;
2702 ASSERT(chunk_pos_ < chunk_size_);
2703 if (chunk_pos_ != 0) {
2704 WriteChunk();
2705 }
2706 stream_->EndOfStream();
2707 }
2708
2709 private:
2710 template<typename T>
AddNumberImpl(T n,const char * format)2711 void AddNumberImpl(T n, const char* format) {
2712 // Buffer for the longest value plus trailing \0
2713 static const int kMaxNumberSize =
2714 MaxDecimalDigitsIn<sizeof(T)>::kUnsigned + 1;
2715 if (chunk_size_ - chunk_pos_ >= kMaxNumberSize) {
2716 int result = SNPrintF(
2717 chunk_.SubVector(chunk_pos_, chunk_size_), format, n);
2718 ASSERT(result != -1);
2719 chunk_pos_ += result;
2720 MaybeWriteChunk();
2721 } else {
2722 EmbeddedVector<char, kMaxNumberSize> buffer;
2723 int result = SNPrintF(buffer, format, n);
2724 USE(result);
2725 ASSERT(result != -1);
2726 AddString(buffer.start());
2727 }
2728 }
MaybeWriteChunk()2729 void MaybeWriteChunk() {
2730 ASSERT(chunk_pos_ <= chunk_size_);
2731 if (chunk_pos_ == chunk_size_) {
2732 WriteChunk();
2733 }
2734 }
WriteChunk()2735 void WriteChunk() {
2736 if (aborted_) return;
2737 if (stream_->WriteAsciiChunk(chunk_.start(), chunk_pos_) ==
2738 v8::OutputStream::kAbort) aborted_ = true;
2739 chunk_pos_ = 0;
2740 }
2741
2742 v8::OutputStream* stream_;
2743 int chunk_size_;
2744 ScopedVector<char> chunk_;
2745 int chunk_pos_;
2746 bool aborted_;
2747 };
2748
2749
2750 // type, name|index, to_node.
2751 const int HeapSnapshotJSONSerializer::kEdgeFieldsCount = 3;
2752 // type, name, id, self_size, edge_count, trace_node_id.
2753 const int HeapSnapshotJSONSerializer::kNodeFieldsCount = 6;
2754
Serialize(v8::OutputStream * stream)2755 void HeapSnapshotJSONSerializer::Serialize(v8::OutputStream* stream) {
2756 if (AllocationTracker* allocation_tracker =
2757 snapshot_->profiler()->allocation_tracker()) {
2758 allocation_tracker->PrepareForSerialization();
2759 }
2760 ASSERT(writer_ == NULL);
2761 writer_ = new OutputStreamWriter(stream);
2762 SerializeImpl();
2763 delete writer_;
2764 writer_ = NULL;
2765 }
2766
2767
SerializeImpl()2768 void HeapSnapshotJSONSerializer::SerializeImpl() {
2769 ASSERT(0 == snapshot_->root()->index());
2770 writer_->AddCharacter('{');
2771 writer_->AddString("\"snapshot\":{");
2772 SerializeSnapshot();
2773 if (writer_->aborted()) return;
2774 writer_->AddString("},\n");
2775 writer_->AddString("\"nodes\":[");
2776 SerializeNodes();
2777 if (writer_->aborted()) return;
2778 writer_->AddString("],\n");
2779 writer_->AddString("\"edges\":[");
2780 SerializeEdges();
2781 if (writer_->aborted()) return;
2782 writer_->AddString("],\n");
2783
2784 writer_->AddString("\"trace_function_infos\":[");
2785 SerializeTraceNodeInfos();
2786 if (writer_->aborted()) return;
2787 writer_->AddString("],\n");
2788 writer_->AddString("\"trace_tree\":[");
2789 SerializeTraceTree();
2790 if (writer_->aborted()) return;
2791 writer_->AddString("],\n");
2792
2793 writer_->AddString("\"strings\":[");
2794 SerializeStrings();
2795 if (writer_->aborted()) return;
2796 writer_->AddCharacter(']');
2797 writer_->AddCharacter('}');
2798 writer_->Finalize();
2799 }
2800
2801
GetStringId(const char * s)2802 int HeapSnapshotJSONSerializer::GetStringId(const char* s) {
2803 HashMap::Entry* cache_entry = strings_.Lookup(
2804 const_cast<char*>(s), StringHash(s), true);
2805 if (cache_entry->value == NULL) {
2806 cache_entry->value = reinterpret_cast<void*>(next_string_id_++);
2807 }
2808 return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
2809 }
2810
2811
2812 namespace {
2813
2814 template<size_t size> struct ToUnsigned;
2815
2816 template<> struct ToUnsigned<4> {
2817 typedef uint32_t Type;
2818 };
2819
2820 template<> struct ToUnsigned<8> {
2821 typedef uint64_t Type;
2822 };
2823
2824 } // namespace
2825
2826
2827 template<typename T>
utoa_impl(T value,const Vector<char> & buffer,int buffer_pos)2828 static int utoa_impl(T value, const Vector<char>& buffer, int buffer_pos) {
2829 STATIC_ASSERT(static_cast<T>(-1) > 0); // Check that T is unsigned
2830 int number_of_digits = 0;
2831 T t = value;
2832 do {
2833 ++number_of_digits;
2834 } while (t /= 10);
2835
2836 buffer_pos += number_of_digits;
2837 int result = buffer_pos;
2838 do {
2839 int last_digit = static_cast<int>(value % 10);
2840 buffer[--buffer_pos] = '0' + last_digit;
2841 value /= 10;
2842 } while (value);
2843 return result;
2844 }
2845
2846
2847 template<typename T>
utoa(T value,const Vector<char> & buffer,int buffer_pos)2848 static int utoa(T value, const Vector<char>& buffer, int buffer_pos) {
2849 typename ToUnsigned<sizeof(value)>::Type unsigned_value = value;
2850 STATIC_ASSERT(sizeof(value) == sizeof(unsigned_value));
2851 return utoa_impl(unsigned_value, buffer, buffer_pos);
2852 }
2853
2854
SerializeEdge(HeapGraphEdge * edge,bool first_edge)2855 void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge,
2856 bool first_edge) {
2857 // The buffer needs space for 3 unsigned ints, 3 commas, \n and \0
2858 static const int kBufferSize =
2859 MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned * 3 + 3 + 2; // NOLINT
2860 EmbeddedVector<char, kBufferSize> buffer;
2861 int edge_name_or_index = edge->type() == HeapGraphEdge::kElement
2862 || edge->type() == HeapGraphEdge::kHidden
2863 ? edge->index() : GetStringId(edge->name());
2864 int buffer_pos = 0;
2865 if (!first_edge) {
2866 buffer[buffer_pos++] = ',';
2867 }
2868 buffer_pos = utoa(edge->type(), buffer, buffer_pos);
2869 buffer[buffer_pos++] = ',';
2870 buffer_pos = utoa(edge_name_or_index, buffer, buffer_pos);
2871 buffer[buffer_pos++] = ',';
2872 buffer_pos = utoa(entry_index(edge->to()), buffer, buffer_pos);
2873 buffer[buffer_pos++] = '\n';
2874 buffer[buffer_pos++] = '\0';
2875 writer_->AddString(buffer.start());
2876 }
2877
2878
SerializeEdges()2879 void HeapSnapshotJSONSerializer::SerializeEdges() {
2880 List<HeapGraphEdge*>& edges = snapshot_->children();
2881 for (int i = 0; i < edges.length(); ++i) {
2882 ASSERT(i == 0 ||
2883 edges[i - 1]->from()->index() <= edges[i]->from()->index());
2884 SerializeEdge(edges[i], i == 0);
2885 if (writer_->aborted()) return;
2886 }
2887 }
2888
2889
SerializeNode(HeapEntry * entry)2890 void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) {
2891 // The buffer needs space for 4 unsigned ints, 1 size_t, 5 commas, \n and \0
2892 static const int kBufferSize =
2893 5 * MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned // NOLINT
2894 + MaxDecimalDigitsIn<sizeof(size_t)>::kUnsigned // NOLINT
2895 + 6 + 1 + 1;
2896 EmbeddedVector<char, kBufferSize> buffer;
2897 int buffer_pos = 0;
2898 if (entry_index(entry) != 0) {
2899 buffer[buffer_pos++] = ',';
2900 }
2901 buffer_pos = utoa(entry->type(), buffer, buffer_pos);
2902 buffer[buffer_pos++] = ',';
2903 buffer_pos = utoa(GetStringId(entry->name()), buffer, buffer_pos);
2904 buffer[buffer_pos++] = ',';
2905 buffer_pos = utoa(entry->id(), buffer, buffer_pos);
2906 buffer[buffer_pos++] = ',';
2907 buffer_pos = utoa(entry->self_size(), buffer, buffer_pos);
2908 buffer[buffer_pos++] = ',';
2909 buffer_pos = utoa(entry->children_count(), buffer, buffer_pos);
2910 buffer[buffer_pos++] = ',';
2911 buffer_pos = utoa(entry->trace_node_id(), buffer, buffer_pos);
2912 buffer[buffer_pos++] = '\n';
2913 buffer[buffer_pos++] = '\0';
2914 writer_->AddString(buffer.start());
2915 }
2916
2917
SerializeNodes()2918 void HeapSnapshotJSONSerializer::SerializeNodes() {
2919 List<HeapEntry>& entries = snapshot_->entries();
2920 for (int i = 0; i < entries.length(); ++i) {
2921 SerializeNode(&entries[i]);
2922 if (writer_->aborted()) return;
2923 }
2924 }
2925
2926
SerializeSnapshot()2927 void HeapSnapshotJSONSerializer::SerializeSnapshot() {
2928 writer_->AddString("\"title\":\"");
2929 writer_->AddString(snapshot_->title());
2930 writer_->AddString("\"");
2931 writer_->AddString(",\"uid\":");
2932 writer_->AddNumber(snapshot_->uid());
2933 writer_->AddString(",\"meta\":");
2934 // The object describing node serialization layout.
2935 // We use a set of macros to improve readability.
2936 #define JSON_A(s) "[" s "]"
2937 #define JSON_O(s) "{" s "}"
2938 #define JSON_S(s) "\"" s "\""
2939 writer_->AddString(JSON_O(
2940 JSON_S("node_fields") ":" JSON_A(
2941 JSON_S("type") ","
2942 JSON_S("name") ","
2943 JSON_S("id") ","
2944 JSON_S("self_size") ","
2945 JSON_S("edge_count") ","
2946 JSON_S("trace_node_id")) ","
2947 JSON_S("node_types") ":" JSON_A(
2948 JSON_A(
2949 JSON_S("hidden") ","
2950 JSON_S("array") ","
2951 JSON_S("string") ","
2952 JSON_S("object") ","
2953 JSON_S("code") ","
2954 JSON_S("closure") ","
2955 JSON_S("regexp") ","
2956 JSON_S("number") ","
2957 JSON_S("native") ","
2958 JSON_S("synthetic") ","
2959 JSON_S("concatenated string") ","
2960 JSON_S("sliced string")) ","
2961 JSON_S("string") ","
2962 JSON_S("number") ","
2963 JSON_S("number") ","
2964 JSON_S("number") ","
2965 JSON_S("number") ","
2966 JSON_S("number")) ","
2967 JSON_S("edge_fields") ":" JSON_A(
2968 JSON_S("type") ","
2969 JSON_S("name_or_index") ","
2970 JSON_S("to_node")) ","
2971 JSON_S("edge_types") ":" JSON_A(
2972 JSON_A(
2973 JSON_S("context") ","
2974 JSON_S("element") ","
2975 JSON_S("property") ","
2976 JSON_S("internal") ","
2977 JSON_S("hidden") ","
2978 JSON_S("shortcut") ","
2979 JSON_S("weak")) ","
2980 JSON_S("string_or_number") ","
2981 JSON_S("node")) ","
2982 JSON_S("trace_function_info_fields") ":" JSON_A(
2983 JSON_S("function_id") ","
2984 JSON_S("name") ","
2985 JSON_S("script_name") ","
2986 JSON_S("script_id") ","
2987 JSON_S("line") ","
2988 JSON_S("column")) ","
2989 JSON_S("trace_node_fields") ":" JSON_A(
2990 JSON_S("id") ","
2991 JSON_S("function_info_index") ","
2992 JSON_S("count") ","
2993 JSON_S("size") ","
2994 JSON_S("children"))));
2995 #undef JSON_S
2996 #undef JSON_O
2997 #undef JSON_A
2998 writer_->AddString(",\"node_count\":");
2999 writer_->AddNumber(snapshot_->entries().length());
3000 writer_->AddString(",\"edge_count\":");
3001 writer_->AddNumber(snapshot_->edges().length());
3002 writer_->AddString(",\"trace_function_count\":");
3003 uint32_t count = 0;
3004 AllocationTracker* tracker = snapshot_->profiler()->allocation_tracker();
3005 if (tracker) {
3006 count = tracker->function_info_list().length();
3007 }
3008 writer_->AddNumber(count);
3009 }
3010
3011
WriteUChar(OutputStreamWriter * w,unibrow::uchar u)3012 static void WriteUChar(OutputStreamWriter* w, unibrow::uchar u) {
3013 static const char hex_chars[] = "0123456789ABCDEF";
3014 w->AddString("\\u");
3015 w->AddCharacter(hex_chars[(u >> 12) & 0xf]);
3016 w->AddCharacter(hex_chars[(u >> 8) & 0xf]);
3017 w->AddCharacter(hex_chars[(u >> 4) & 0xf]);
3018 w->AddCharacter(hex_chars[u & 0xf]);
3019 }
3020
3021
SerializeTraceTree()3022 void HeapSnapshotJSONSerializer::SerializeTraceTree() {
3023 AllocationTracker* tracker = snapshot_->profiler()->allocation_tracker();
3024 if (!tracker) return;
3025 AllocationTraceTree* traces = tracker->trace_tree();
3026 SerializeTraceNode(traces->root());
3027 }
3028
3029
SerializeTraceNode(AllocationTraceNode * node)3030 void HeapSnapshotJSONSerializer::SerializeTraceNode(AllocationTraceNode* node) {
3031 // The buffer needs space for 4 unsigned ints, 4 commas, [ and \0
3032 const int kBufferSize =
3033 4 * MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned // NOLINT
3034 + 4 + 1 + 1;
3035 EmbeddedVector<char, kBufferSize> buffer;
3036 int buffer_pos = 0;
3037 buffer_pos = utoa(node->id(), buffer, buffer_pos);
3038 buffer[buffer_pos++] = ',';
3039 buffer_pos = utoa(node->function_info_index(), buffer, buffer_pos);
3040 buffer[buffer_pos++] = ',';
3041 buffer_pos = utoa(node->allocation_count(), buffer, buffer_pos);
3042 buffer[buffer_pos++] = ',';
3043 buffer_pos = utoa(node->allocation_size(), buffer, buffer_pos);
3044 buffer[buffer_pos++] = ',';
3045 buffer[buffer_pos++] = '[';
3046 buffer[buffer_pos++] = '\0';
3047 writer_->AddString(buffer.start());
3048
3049 Vector<AllocationTraceNode*> children = node->children();
3050 for (int i = 0; i < children.length(); i++) {
3051 if (i > 0) {
3052 writer_->AddCharacter(',');
3053 }
3054 SerializeTraceNode(children[i]);
3055 }
3056 writer_->AddCharacter(']');
3057 }
3058
3059
3060 // 0-based position is converted to 1-based during the serialization.
SerializePosition(int position,const Vector<char> & buffer,int buffer_pos)3061 static int SerializePosition(int position, const Vector<char>& buffer,
3062 int buffer_pos) {
3063 if (position == -1) {
3064 buffer[buffer_pos++] = '0';
3065 } else {
3066 ASSERT(position >= 0);
3067 buffer_pos = utoa(static_cast<unsigned>(position + 1), buffer, buffer_pos);
3068 }
3069 return buffer_pos;
3070 }
3071
3072
SerializeTraceNodeInfos()3073 void HeapSnapshotJSONSerializer::SerializeTraceNodeInfos() {
3074 AllocationTracker* tracker = snapshot_->profiler()->allocation_tracker();
3075 if (!tracker) return;
3076 // The buffer needs space for 6 unsigned ints, 6 commas, \n and \0
3077 const int kBufferSize =
3078 6 * MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned // NOLINT
3079 + 6 + 1 + 1;
3080 EmbeddedVector<char, kBufferSize> buffer;
3081 const List<AllocationTracker::FunctionInfo*>& list =
3082 tracker->function_info_list();
3083 bool first_entry = true;
3084 for (int i = 0; i < list.length(); i++) {
3085 AllocationTracker::FunctionInfo* info = list[i];
3086 int buffer_pos = 0;
3087 if (first_entry) {
3088 first_entry = false;
3089 } else {
3090 buffer[buffer_pos++] = ',';
3091 }
3092 buffer_pos = utoa(info->function_id, buffer, buffer_pos);
3093 buffer[buffer_pos++] = ',';
3094 buffer_pos = utoa(GetStringId(info->name), buffer, buffer_pos);
3095 buffer[buffer_pos++] = ',';
3096 buffer_pos = utoa(GetStringId(info->script_name), buffer, buffer_pos);
3097 buffer[buffer_pos++] = ',';
3098 // The cast is safe because script id is a non-negative Smi.
3099 buffer_pos = utoa(static_cast<unsigned>(info->script_id), buffer,
3100 buffer_pos);
3101 buffer[buffer_pos++] = ',';
3102 buffer_pos = SerializePosition(info->line, buffer, buffer_pos);
3103 buffer[buffer_pos++] = ',';
3104 buffer_pos = SerializePosition(info->column, buffer, buffer_pos);
3105 buffer[buffer_pos++] = '\n';
3106 buffer[buffer_pos++] = '\0';
3107 writer_->AddString(buffer.start());
3108 }
3109 }
3110
3111
SerializeString(const unsigned char * s)3112 void HeapSnapshotJSONSerializer::SerializeString(const unsigned char* s) {
3113 writer_->AddCharacter('\n');
3114 writer_->AddCharacter('\"');
3115 for ( ; *s != '\0'; ++s) {
3116 switch (*s) {
3117 case '\b':
3118 writer_->AddString("\\b");
3119 continue;
3120 case '\f':
3121 writer_->AddString("\\f");
3122 continue;
3123 case '\n':
3124 writer_->AddString("\\n");
3125 continue;
3126 case '\r':
3127 writer_->AddString("\\r");
3128 continue;
3129 case '\t':
3130 writer_->AddString("\\t");
3131 continue;
3132 case '\"':
3133 case '\\':
3134 writer_->AddCharacter('\\');
3135 writer_->AddCharacter(*s);
3136 continue;
3137 default:
3138 if (*s > 31 && *s < 128) {
3139 writer_->AddCharacter(*s);
3140 } else if (*s <= 31) {
3141 // Special character with no dedicated literal.
3142 WriteUChar(writer_, *s);
3143 } else {
3144 // Convert UTF-8 into \u UTF-16 literal.
3145 unsigned length = 1, cursor = 0;
3146 for ( ; length <= 4 && *(s + length) != '\0'; ++length) { }
3147 unibrow::uchar c = unibrow::Utf8::CalculateValue(s, length, &cursor);
3148 if (c != unibrow::Utf8::kBadChar) {
3149 WriteUChar(writer_, c);
3150 ASSERT(cursor != 0);
3151 s += cursor - 1;
3152 } else {
3153 writer_->AddCharacter('?');
3154 }
3155 }
3156 }
3157 }
3158 writer_->AddCharacter('\"');
3159 }
3160
3161
SerializeStrings()3162 void HeapSnapshotJSONSerializer::SerializeStrings() {
3163 ScopedVector<const unsigned char*> sorted_strings(
3164 strings_.occupancy() + 1);
3165 for (HashMap::Entry* entry = strings_.Start();
3166 entry != NULL;
3167 entry = strings_.Next(entry)) {
3168 int index = static_cast<int>(reinterpret_cast<uintptr_t>(entry->value));
3169 sorted_strings[index] = reinterpret_cast<const unsigned char*>(entry->key);
3170 }
3171 writer_->AddString("\"<dummy>\"");
3172 for (int i = 1; i < sorted_strings.length(); ++i) {
3173 writer_->AddCharacter(',');
3174 SerializeString(sorted_strings[i]);
3175 if (writer_->aborted()) return;
3176 }
3177 }
3178
3179
3180 } } // namespace v8::internal
3181