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