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