• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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