LCOV - code coverage report
Current view: top level - src/profiler - heap-snapshot-generator.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 1122 1358 82.6 %
Date: 2019-01-20 Functions: 137 187 73.3 %

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

Generated by: LCOV version 1.10