LCOV - code coverage report
Current view: top level - src/profiler - heap-snapshot-generator.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 1101 1265 87.0 %
Date: 2017-10-20 Functions: 136 173 78.6 %

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

Generated by: LCOV version 1.10