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