heap-snapshot-generator.cc 109 KB
Newer Older
1
// Copyright 2013 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5
#include "src/profiler/heap-snapshot-generator.h"
6

7 8
#include <utility>

9
#include "src/api/api-inl.h"
10
#include "src/base/optional.h"
11
#include "src/base/vector.h"
12
#include "src/codegen/assembler-inl.h"
13
#include "src/common/globals.h"
14
#include "src/debug/debug.h"
15
#include "src/handles/global-handles.h"
16
#include "src/heap/combined-heap.h"
17
#include "src/heap/safepoint.h"
18
#include "src/numbers/conversions.h"
19
#include "src/objects/allocation-site-inl.h"
20
#include "src/objects/api-callbacks.h"
21
#include "src/objects/cell-inl.h"
22
#include "src/objects/feedback-cell-inl.h"
23
#include "src/objects/hash-table-inl.h"
24
#include "src/objects/js-array-buffer-inl.h"
25
#include "src/objects/js-array-inl.h"
26
#include "src/objects/js-collection-inl.h"
27
#include "src/objects/js-generator-inl.h"
28 29
#include "src/objects/js-promise-inl.h"
#include "src/objects/js-regexp-inl.h"
30
#include "src/objects/literal-objects-inl.h"
31
#include "src/objects/objects-body-descriptors.h"
32
#include "src/objects/objects-inl.h"
33
#include "src/objects/prototype.h"
34
#include "src/objects/slots-inl.h"
35
#include "src/objects/struct-inl.h"
36
#include "src/objects/transitions-inl.h"
37
#include "src/objects/visitors.h"
38 39 40
#include "src/profiler/allocation-tracker.h"
#include "src/profiler/heap-profiler.h"
#include "src/profiler/heap-snapshot-generator-inl.h"
41 42 43 44

namespace v8 {
namespace internal {

45 46 47 48 49
HeapGraphEdge::HeapGraphEdge(Type type, const char* name, HeapEntry* from,
                             HeapEntry* to)
    : bit_field_(TypeField::encode(type) |
                 FromIndexField::encode(from->index())),
      to_entry_(to),
50
      name_(name) {
51
  DCHECK(type == kContextVariable
52 53
      || type == kProperty
      || type == kInternal
54 55
      || type == kShortcut
      || type == kWeak);
56 57
}

58 59 60 61 62
HeapGraphEdge::HeapGraphEdge(Type type, int index, HeapEntry* from,
                             HeapEntry* to)
    : bit_field_(TypeField::encode(type) |
                 FromIndexField::encode(from->index())),
      to_entry_(to),
63
      index_(index) {
64
  DCHECK(type == kElement || type == kHidden);
65 66
}

67 68
HeapEntry::HeapEntry(HeapSnapshot* snapshot, int index, Type type,
                     const char* name, SnapshotObjectId id, size_t self_size,
69
                     unsigned trace_node_id)
70
    : type_(type),
71
      index_(index),
72
      children_count_(0),
73 74
      self_size_(self_size),
      snapshot_(snapshot),
75 76
      name_(name),
      id_(id),
77 78 79
      trace_node_id_(trace_node_id) {
  DCHECK_GE(index, 0);
}
80 81 82 83 84

void HeapEntry::SetNamedReference(HeapGraphEdge::Type type,
                                  const char* name,
                                  HeapEntry* entry) {
  ++children_count_;
85
  snapshot_->edges().emplace_back(type, name, this, entry);
86 87 88 89 90 91
}

void HeapEntry::SetIndexedReference(HeapGraphEdge::Type type,
                                    int index,
                                    HeapEntry* entry) {
  ++children_count_;
92
  snapshot_->edges().emplace_back(type, index, this, entry);
93 94
}

95 96 97 98 99 100 101 102 103 104 105
void HeapEntry::SetNamedAutoIndexReference(HeapGraphEdge::Type type,
                                           const char* description,
                                           HeapEntry* child,
                                           StringsStorage* names) {
  int index = children_count_ + 1;
  const char* name = description
                         ? names->GetFormatted("%d / %s", index, description)
                         : names->GetName(index);
  SetNamedReference(type, name, child);
}

106 107
void HeapEntry::Print(const char* prefix, const char* edge_name, int max_depth,
                      int indent) const {
108
  STATIC_ASSERT(sizeof(unsigned) == sizeof(id()));
109
  base::OS::Print("%6zu @%6u %*c %s%s: ", self_size(), id(), indent, ' ',
110
                  prefix, edge_name);
111
  if (type() != kString) {
112
    base::OS::Print("%s %.40s\n", TypeAsString(), name_);
113
  } else {
114
    base::OS::Print("\"");
115 116 117
    const char* c = name_;
    while (*c && (c - name_) <= 40) {
      if (*c != '\n')
118
        base::OS::Print("%c", *c);
119
      else
120
        base::OS::Print("\\n");
121 122
      ++c;
    }
123
    base::OS::Print("\"\n");
124 125
  }
  if (--max_depth == 0) return;
126 127
  for (auto i = children_begin(); i != children_end(); ++i) {
    HeapGraphEdge& edge = **i;
128
    const char* edge_prefix = "";
129
    base::EmbeddedVector<char, 64> index;
130
    const char* edge_name = index.begin();
131 132 133 134 135 136
    switch (edge.type()) {
      case HeapGraphEdge::kContextVariable:
        edge_prefix = "#";
        edge_name = edge.name();
        break;
      case HeapGraphEdge::kElement:
137
        SNPrintF(index, "%d", edge.index());
138 139 140 141 142 143 144 145 146 147
        break;
      case HeapGraphEdge::kInternal:
        edge_prefix = "$";
        edge_name = edge.name();
        break;
      case HeapGraphEdge::kProperty:
        edge_name = edge.name();
        break;
      case HeapGraphEdge::kHidden:
        edge_prefix = "$";
148
        SNPrintF(index, "%d", edge.index());
149 150 151 152 153 154 155
        break;
      case HeapGraphEdge::kShortcut:
        edge_prefix = "^";
        edge_name = edge.name();
        break;
      case HeapGraphEdge::kWeak:
        edge_prefix = "w";
156
        edge_name = edge.name();
157 158
        break;
      default:
159
        SNPrintF(index, "!!! unknown edge type: %d ", edge.type());
160 161 162 163 164
    }
    edge.to()->Print(edge_prefix, edge_name, max_depth, indent + 2);
  }
}

165
const char* HeapEntry::TypeAsString() const {
166 167 168 169 170 171 172 173 174 175 176
  switch (type()) {
    case kHidden: return "/hidden/";
    case kObject: return "/object/";
    case kClosure: return "/closure/";
    case kString: return "/string/";
    case kCode: return "/code/";
    case kArray: return "/array/";
    case kRegExp: return "/regexp/";
    case kHeapNumber: return "/number/";
    case kNative: return "/native/";
    case kSynthetic: return "/synthetic/";
177 178
    case kConsString: return "/concatenated string/";
    case kSlicedString: return "/sliced string/";
179
    case kSymbol: return "/symbol/";
180 181
    case kBigInt:
      return "/bigint/";
182 183 184 185
    default: return "???";
  }
}

Arno Renevier's avatar
Arno Renevier committed
186 187
HeapSnapshot::HeapSnapshot(HeapProfiler* profiler, bool global_objects_as_roots,
                           bool capture_numeric_value)
188
    : profiler_(profiler),
Arno Renevier's avatar
Arno Renevier committed
189 190
      treat_global_objects_as_roots_(global_objects_as_roots),
      capture_numeric_value_(capture_numeric_value) {
191 192
  // It is very important to keep objects that form a heap snapshot
  // as small as possible. Check assumptions about data structure sizes.
193 194 195 196 197 198 199 200
  STATIC_ASSERT(kSystemPointerSize != 4 || sizeof(HeapGraphEdge) == 12);
  STATIC_ASSERT(kSystemPointerSize != 8 || sizeof(HeapGraphEdge) == 24);
  STATIC_ASSERT(kSystemPointerSize != 4 || sizeof(HeapEntry) == 32);
#if V8_CC_MSVC
  STATIC_ASSERT(kSystemPointerSize != 8 || sizeof(HeapEntry) == 48);
#else   // !V8_CC_MSVC
  STATIC_ASSERT(kSystemPointerSize != 8 || sizeof(HeapEntry) == 40);
#endif  // !V8_CC_MSVC
201
  memset(&gc_subroot_entries_, 0, sizeof(gc_subroot_entries_));
202 203 204
}

void HeapSnapshot::Delete() {
205
  profiler_->RemoveSnapshot(this);
206 207 208
}

void HeapSnapshot::RememberLastJSObjectId() {
209
  max_snapshot_js_object_id_ = profiler_->heap_object_map()->last_assigned_id();
210 211
}

212 213 214 215
void HeapSnapshot::AddSyntheticRootEntries() {
  AddRootEntry();
  AddGcRootsEntry();
  SnapshotObjectId id = HeapObjectsMap::kGcRootsFirstSubrootId;
216 217
  for (int root = 0; root < static_cast<int>(Root::kNumberOfRoots); root++) {
    AddGcSubrootEntry(static_cast<Root>(root), id);
218 219
    id += HeapObjectsMap::kObjectIdStep;
  }
220
  DCHECK_EQ(HeapObjectsMap::kFirstAvailableObjectId, id);
221 222
}

223 224
void HeapSnapshot::AddRootEntry() {
  DCHECK_NULL(root_entry_);
225
  DCHECK(entries_.empty());  // Root entry must be the first one.
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
  root_entry_ = AddEntry(HeapEntry::kSynthetic, "",
                         HeapObjectsMap::kInternalRootObjectId, 0, 0);
  DCHECK_EQ(1u, entries_.size());
  DCHECK_EQ(root_entry_, &entries_.front());
}

void HeapSnapshot::AddGcRootsEntry() {
  DCHECK_NULL(gc_roots_entry_);
  gc_roots_entry_ = AddEntry(HeapEntry::kSynthetic, "(GC roots)",
                             HeapObjectsMap::kGcRootsObjectId, 0, 0);
}

void HeapSnapshot::AddGcSubrootEntry(Root root, SnapshotObjectId id) {
  DCHECK_NULL(gc_subroot_entries_[static_cast<int>(root)]);
  gc_subroot_entries_[static_cast<int>(root)] =
241
      AddEntry(HeapEntry::kSynthetic, RootVisitor::RootName(root), id, 0, 0);
242 243
}

244 245 246
void HeapSnapshot::AddLocation(HeapEntry* entry, int scriptId, int line,
                               int col) {
  locations_.emplace_back(entry->index(), scriptId, line, col);
247
}
248 249 250 251

HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
                                  const char* name,
                                  SnapshotObjectId id,
252 253
                                  size_t size,
                                  unsigned trace_node_id) {
254 255 256
  DCHECK(!is_complete());
  entries_.emplace_back(this, static_cast<int>(entries_.size()), type, name, id,
                        size, trace_node_id);
257
  return &entries_.back();
258 259 260
}

void HeapSnapshot::FillChildren() {
261
  DCHECK(children().empty());
262
  int children_index = 0;
263 264
  for (HeapEntry& entry : entries()) {
    children_index = entry.set_children_index(children_index);
265
  }
266
  DCHECK_EQ(edges().size(), static_cast<size_t>(children_index));
267
  children().resize(edges().size());
268 269
  for (HeapGraphEdge& edge : edges()) {
    edge.from()->add_child(&edge);
270 271 272 273
  }
}

HeapEntry* HeapSnapshot::GetEntryById(SnapshotObjectId id) {
274 275 276
  if (entries_by_id_cache_.empty()) {
    CHECK(is_complete());
    entries_by_id_cache_.reserve(entries_.size());
277
    for (HeapEntry& entry : entries_) {
278
      entries_by_id_cache_.emplace(entry.id(), &entry);
279 280
    }
  }
281 282
  auto it = entries_by_id_cache_.find(id);
  return it != entries_by_id_cache_.end() ? it->second : nullptr;
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
}

void HeapSnapshot::Print(int max_depth) {
  root()->Print("", "", max_depth, 0);
}

// We split IDs on evens for embedder objects (see
// HeapObjectsMap::GenerateId) and odds for native objects.
const SnapshotObjectId HeapObjectsMap::kInternalRootObjectId = 1;
const SnapshotObjectId HeapObjectsMap::kGcRootsObjectId =
    HeapObjectsMap::kInternalRootObjectId + HeapObjectsMap::kObjectIdStep;
const SnapshotObjectId HeapObjectsMap::kGcRootsFirstSubrootId =
    HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep;
const SnapshotObjectId HeapObjectsMap::kFirstAvailableObjectId =
    HeapObjectsMap::kGcRootsFirstSubrootId +
298
    static_cast<int>(Root::kNumberOfRoots) * HeapObjectsMap::kObjectIdStep;
299 300

HeapObjectsMap::HeapObjectsMap(Heap* heap)
301
    : next_id_(kFirstAvailableObjectId), heap_(heap) {
302 303 304 305
  // The dummy element at zero index is needed as entries_map_ cannot hold
  // an entry with zero value. Otherwise it's impossible to tell if
  // LookupOrInsert has added a new item or just returning exisiting one
  // having the value of zero.
306
  entries_.emplace_back(0, kNullAddress, 0, true);
307 308
}

309
bool HeapObjectsMap::MoveObject(Address from, Address to, int object_size) {
310 311
  DCHECK_NE(kNullAddress, to);
  DCHECK_NE(kNullAddress, from);
312
  if (from == to) return false;
313 314
  void* from_value = entries_map_.Remove(reinterpret_cast<void*>(from),
                                         ComputeAddressHash(from));
315
  if (from_value == nullptr) {
316 317 318
    // It may occur that some untracked object moves to an address X and there
    // is a tracked object at that address. In this case we should remove the
    // entry as we know that the object has died.
319 320
    void* to_value = entries_map_.Remove(reinterpret_cast<void*>(to),
                                         ComputeAddressHash(to));
321
    if (to_value != nullptr) {
322 323
      int to_entry_info_index =
          static_cast<int>(reinterpret_cast<intptr_t>(to_value));
324
      entries_.at(to_entry_info_index).addr = kNullAddress;
325 326
    }
  } else {
327 328
    base::HashMap::Entry* to_entry = entries_map_.LookupOrInsert(
        reinterpret_cast<void*>(to), ComputeAddressHash(to));
329
    if (to_entry->value != nullptr) {
330 331 332 333 334 335 336
      // We found the existing entry with to address for an old object.
      // Without this operation we will have two EntryInfo's with the same
      // value in addr field. It is bad because later at RemoveDeadEntries
      // one of this entry will be removed with the corresponding entries_map_
      // entry.
      int to_entry_info_index =
          static_cast<int>(reinterpret_cast<intptr_t>(to_entry->value));
337
      entries_.at(to_entry_info_index).addr = kNullAddress;
338 339 340 341
    }
    int from_entry_info_index =
        static_cast<int>(reinterpret_cast<intptr_t>(from_value));
    entries_.at(from_entry_info_index).addr = to;
342 343 344
    // Size of an object can change during its life, so to keep information
    // about the object in entries_ consistent, we have to adjust size when the
    // object is migrated.
345 346
    if (FLAG_heap_profiler_trace_objects) {
      PrintF("Move object from %p to %p old size %6d new size %6d\n",
347
             reinterpret_cast<void*>(from), reinterpret_cast<void*>(to),
348
             entries_.at(from_entry_info_index).size, object_size);
349
    }
350
    entries_.at(from_entry_info_index).size = object_size;
351
    to_entry->value = from_value;
352
  }
353
  return from_value != nullptr;
354 355 356
}


357 358 359 360 361
void HeapObjectsMap::UpdateObjectSize(Address addr, int size) {
  FindOrAddEntry(addr, size, false);
}


362
SnapshotObjectId HeapObjectsMap::FindEntry(Address addr) {
363 364
  base::HashMap::Entry* entry = entries_map_.Lookup(
      reinterpret_cast<void*>(addr), ComputeAddressHash(addr));
365
  if (entry == nullptr) return v8::HeapProfiler::kUnknownObjectId;
366 367
  int entry_index = static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
  EntryInfo& entry_info = entries_.at(entry_index);
368
  DCHECK(static_cast<uint32_t>(entries_.size()) > entries_map_.occupancy());
369 370 371 372 373
  return entry_info.id;
}


SnapshotObjectId HeapObjectsMap::FindOrAddEntry(Address addr,
374 375
                                                unsigned int size,
                                                bool accessed) {
376
  DCHECK(static_cast<uint32_t>(entries_.size()) > entries_map_.occupancy());
377 378
  base::HashMap::Entry* entry = entries_map_.LookupOrInsert(
      reinterpret_cast<void*>(addr), ComputeAddressHash(addr));
379
  if (entry->value != nullptr) {
380 381 382
    int entry_index =
        static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
    EntryInfo& entry_info = entries_.at(entry_index);
383
    entry_info.accessed = accessed;
384 385
    if (FLAG_heap_profiler_trace_objects) {
      PrintF("Update object size : %p with old size %d and new size %d\n",
386
             reinterpret_cast<void*>(addr), entry_info.size, size);
387
    }
388 389 390
    entry_info.size = size;
    return entry_info.id;
  }
391
  entry->value = reinterpret_cast<void*>(entries_.size());
Arno Renevier's avatar
Arno Renevier committed
392
  SnapshotObjectId id = get_next_id();
393 394
  entries_.push_back(EntryInfo(id, addr, size, accessed));
  DCHECK(static_cast<uint32_t>(entries_.size()) > entries_map_.occupancy());
395 396 397
  return id;
}

398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
SnapshotObjectId HeapObjectsMap::FindMergedNativeEntry(NativeObject addr) {
  auto it = merged_native_entries_map_.find(addr);
  if (it == merged_native_entries_map_.end())
    return v8::HeapProfiler::kUnknownObjectId;
  return entries_[it->second].id;
}

void HeapObjectsMap::AddMergedNativeEntry(NativeObject addr,
                                          Address canonical_addr) {
  base::HashMap::Entry* entry =
      entries_map_.Lookup(reinterpret_cast<void*>(canonical_addr),
                          ComputeAddressHash(canonical_addr));
  auto result = merged_native_entries_map_.insert(
      {addr, reinterpret_cast<size_t>(entry->value)});
  if (!result.second) {
    result.first->second = reinterpret_cast<size_t>(entry->value);
  }
}

417
void HeapObjectsMap::StopHeapObjectsTracking() { time_intervals_.clear(); }
418

419
void HeapObjectsMap::UpdateHeapObjectsMap() {
420 421 422 423
  if (FLAG_heap_profiler_trace_objects) {
    PrintF("Begin HeapObjectsMap::UpdateHeapObjectsMap. map has %d entries.\n",
           entries_map_.occupancy());
  }
424 425
  heap_->PreciseCollectAllGarbage(Heap::kNoGCFlags,
                                  GarbageCollectionReason::kHeapProfiler);
426
  CombinedHeapObjectIterator iterator(heap_);
427 428
  for (HeapObject obj = iterator.Next(); !obj.is_null();
       obj = iterator.Next()) {
429
    FindOrAddEntry(obj.address(), obj.Size());
430 431
    if (FLAG_heap_profiler_trace_objects) {
      PrintF("Update object      : %p %6d. Next address is %p\n",
432 433
             reinterpret_cast<void*>(obj.address()), obj.Size(),
             reinterpret_cast<void*>(obj.address() + obj.Size()));
434
    }
435 436
  }
  RemoveDeadEntries();
437 438 439 440 441 442
  if (FLAG_heap_profiler_trace_objects) {
    PrintF("End HeapObjectsMap::UpdateHeapObjectsMap. map has %d entries.\n",
           entries_map_.occupancy());
  }
}

443 444
SnapshotObjectId HeapObjectsMap::PushHeapObjectsStats(OutputStream* stream,
                                                      int64_t* timestamp_us) {
445
  UpdateHeapObjectsMap();
446
  time_intervals_.emplace_back(next_id_);
447
  int prefered_chunk_size = stream->GetChunkSize();
448
  std::vector<v8::HeapStatsUpdate> stats_buffer;
449 450 451
  DCHECK(!entries_.empty());
  EntryInfo* entry_info = &entries_.front();
  EntryInfo* end_entry_info = &entries_.back() + 1;
452 453
  for (size_t time_interval_index = 0;
       time_interval_index < time_intervals_.size(); ++time_interval_index) {
454 455 456 457 458 459 460 461 462 463 464 465
    TimeInterval& time_interval = time_intervals_[time_interval_index];
    SnapshotObjectId time_interval_id = time_interval.id;
    uint32_t entries_size = 0;
    EntryInfo* start_entry_info = entry_info;
    while (entry_info < end_entry_info && entry_info->id < time_interval_id) {
      entries_size += entry_info->size;
      ++entry_info;
    }
    uint32_t entries_count =
        static_cast<uint32_t>(entry_info - start_entry_info);
    if (time_interval.count != entries_count ||
        time_interval.size != entries_size) {
466 467 468 469
      stats_buffer.emplace_back(static_cast<uint32_t>(time_interval_index),
                                time_interval.count = entries_count,
                                time_interval.size = entries_size);
      if (static_cast<int>(stats_buffer.size()) >= prefered_chunk_size) {
470
        OutputStream::WriteResult result = stream->WriteHeapStatsChunk(
471
            &stats_buffer.front(), static_cast<int>(stats_buffer.size()));
472
        if (result == OutputStream::kAbort) return last_assigned_id();
473
        stats_buffer.clear();
474 475 476
      }
    }
  }
477
  DCHECK(entry_info == end_entry_info);
478
  if (!stats_buffer.empty()) {
479
    OutputStream::WriteResult result = stream->WriteHeapStatsChunk(
480
        &stats_buffer.front(), static_cast<int>(stats_buffer.size()));
481 482 483
    if (result == OutputStream::kAbort) return last_assigned_id();
  }
  stream->EndOfStream();
484
  if (timestamp_us) {
485 486 487
    *timestamp_us =
        (time_intervals_.back().timestamp - time_intervals_.front().timestamp)
            .InMicroseconds();
488
  }
489 490 491 492 493
  return last_assigned_id();
}


void HeapObjectsMap::RemoveDeadEntries() {
494
  DCHECK(entries_.size() > 0 && entries_.at(0).id == 0 &&
495
         entries_.at(0).addr == kNullAddress);
496 497 498 499 500 501 502 503 504 505

  // Build up temporary reverse map.
  std::unordered_map<size_t, NativeObject> reverse_merged_native_entries_map;
  for (const auto& it : merged_native_entries_map_) {
    auto result =
        reverse_merged_native_entries_map.emplace(it.second, it.first);
    DCHECK(result.second);
    USE(result);
  }

506 507
  size_t first_free_entry = 1;
  for (size_t i = 1; i < entries_.size(); ++i) {
508
    EntryInfo& entry_info = entries_.at(i);
509
    auto merged_reverse_it = reverse_merged_native_entries_map.find(i);
510 511 512 513 514
    if (entry_info.accessed) {
      if (first_free_entry != i) {
        entries_.at(first_free_entry) = entry_info;
      }
      entries_.at(first_free_entry).accessed = false;
515 516 517
      base::HashMap::Entry* entry =
          entries_map_.Lookup(reinterpret_cast<void*>(entry_info.addr),
                              ComputeAddressHash(entry_info.addr));
518
      DCHECK(entry);
519
      entry->value = reinterpret_cast<void*>(first_free_entry);
520 521 522 523 524
      if (merged_reverse_it != reverse_merged_native_entries_map.end()) {
        auto it = merged_native_entries_map_.find(merged_reverse_it->second);
        DCHECK_NE(merged_native_entries_map_.end(), it);
        it->second = first_free_entry;
      }
525 526 527
      ++first_free_entry;
    } else {
      if (entry_info.addr) {
528 529
        entries_map_.Remove(reinterpret_cast<void*>(entry_info.addr),
                            ComputeAddressHash(entry_info.addr));
530 531 532
        if (merged_reverse_it != reverse_merged_native_entries_map.end()) {
          merged_native_entries_map_.erase(merged_reverse_it->second);
        }
533 534 535
      }
    }
  }
536 537 538
  entries_.erase(entries_.begin() + first_free_entry, entries_.end());

  DCHECK(static_cast<uint32_t>(entries_.size()) - 1 ==
539 540 541
         entries_map_.occupancy());
}

542 543 544
V8HeapExplorer::V8HeapExplorer(HeapSnapshot* snapshot,
                               SnapshottingProgressReportingInterface* progress,
                               v8::HeapProfiler::ObjectNameResolver* resolver)
545
    : heap_(snapshot->profiler()->heap_object_map()->heap()),
546
      snapshot_(snapshot),
547 548
      names_(snapshot_->profiler()->names()),
      heap_object_map_(snapshot_->profiler()->heap_object_map()),
549
      progress_(progress),
550
      generator_(nullptr),
551
      global_object_name_resolver_(resolver) {}
552 553

HeapEntry* V8HeapExplorer::AllocateEntry(HeapThing ptr) {
554
  return AddEntry(HeapObject::cast(Object(reinterpret_cast<Address>(ptr))));
555 556
}

Arno Renevier's avatar
Arno Renevier committed
557 558 559 560 561 562 563 564 565 566
HeapEntry* V8HeapExplorer::AllocateEntry(Smi smi) {
  SnapshotObjectId id = heap_object_map_->get_next_id();
  HeapEntry* entry =
      snapshot_->AddEntry(HeapEntry::kHeapNumber, "smi number", id, 0, 0);
  // XXX: Smis do not appear in CombinedHeapObjectIterator, so we need to
  // extract the references here
  ExtractNumberReference(entry, smi);
  return entry;
}

567
void V8HeapExplorer::ExtractLocation(HeapEntry* entry, HeapObject object) {
568
  if (object.IsJSFunction()) {
569
    JSFunction func = JSFunction::cast(object);
570
    ExtractLocationForJSFunction(entry, func);
571

572
  } else if (object.IsJSGeneratorObject()) {
573
    JSGeneratorObject gen = JSGeneratorObject::cast(object);
574
    ExtractLocationForJSFunction(entry, gen.function());
575

576
  } else if (object.IsJSObject()) {
577 578
    JSObject obj = JSObject::cast(object);
    JSFunction maybe_constructor = GetConstructor(obj);
579

580
    if (!maybe_constructor.is_null()) {
581
      ExtractLocationForJSFunction(entry, maybe_constructor);
582
    }
583 584 585
  }
}

586
void V8HeapExplorer::ExtractLocationForJSFunction(HeapEntry* entry,
587
                                                  JSFunction func) {
588 589 590 591
  if (!func.shared().script().IsScript()) return;
  Script script = Script::cast(func.shared().script());
  int scriptId = script.id();
  int start = func.shared().StartPosition();
592 593 594
  Script::PositionInfo info;
  script.GetPositionInfo(start, &info, Script::WITH_OFFSET);
  snapshot_->AddLocation(entry, scriptId, info.line, info.column);
595
}
596

597
HeapEntry* V8HeapExplorer::AddEntry(HeapObject object) {
598
  if (object.IsJSFunction()) {
599
    JSFunction func = JSFunction::cast(object);
600 601
    SharedFunctionInfo shared = func.shared();
    const char* name = names_->GetName(shared.Name());
602
    return AddEntry(object, HeapEntry::kClosure, name);
603
  } else if (object.IsJSBoundFunction()) {
604
    return AddEntry(object, HeapEntry::kClosure, "native_bind");
605
  } else if (object.IsJSRegExp()) {
606
    JSRegExp re = JSRegExp::cast(object);
607 608
    return AddEntry(object, HeapEntry::kRegExp, names_->GetName(re.Pattern()));
  } else if (object.IsJSObject()) {
609
    const char* name = names_->GetName(
610
        GetConstructorName(JSObject::cast(object)));
611
    if (object.IsJSGlobalObject()) {
612 613
      auto it = global_object_tag_map_.find(JSGlobalObject::cast(object));
      if (it != global_object_tag_map_.end()) {
614
        name = names_->GetFormatted("%s / %s", name, it->second);
615 616 617
      }
    }
    return AddEntry(object, HeapEntry::kObject, name);
618
  } else if (object.IsString()) {
619
    String string = String::cast(object);
620
    if (string.IsConsString()) {
621
      return AddEntry(object, HeapEntry::kConsString, "(concatenated string)");
622
    } else if (string.IsSlicedString()) {
623 624 625 626 627
      return AddEntry(object, HeapEntry::kSlicedString, "(sliced string)");
    } else {
      return AddEntry(object, HeapEntry::kString,
                      names_->GetName(String::cast(object)));
    }
628 629
  } else if (object.IsSymbol()) {
    if (Symbol::cast(object).is_private())
630 631 632
      return AddEntry(object, HeapEntry::kHidden, "private symbol");
    else
      return AddEntry(object, HeapEntry::kSymbol, "symbol");
633
  } else if (object.IsBigInt()) {
634
    return AddEntry(object, HeapEntry::kBigInt, "bigint");
635
  } else if (object.IsCode()) {
636
    return AddEntry(object, HeapEntry::kCode, "");
637 638
  } else if (object.IsSharedFunctionInfo()) {
    String name = SharedFunctionInfo::cast(object).Name();
639
    return AddEntry(object, HeapEntry::kCode, names_->GetName(name));
640 641 642 643 644
  } else if (object.IsScript()) {
    Object name = Script::cast(object).name();
    return AddEntry(object, HeapEntry::kCode,
                    name.IsString() ? names_->GetName(String::cast(name)) : "");
  } else if (object.IsNativeContext()) {
645
    return AddEntry(object, HeapEntry::kHidden, "system / NativeContext");
646
  } else if (object.IsContext()) {
647
    return AddEntry(object, HeapEntry::kObject, "system / Context");
648
  } else if (object.IsFixedArray() || object.IsFixedDoubleArray() ||
649
             object.IsByteArray()) {
650
    return AddEntry(object, HeapEntry::kArray, "");
651
  } else if (object.IsHeapNumber()) {
Arno Renevier's avatar
Arno Renevier committed
652
    return AddEntry(object, HeapEntry::kHeapNumber, "heap number");
653 654 655 656
  }
  return AddEntry(object, HeapEntry::kHidden, GetSystemEntryName(object));
}

657
HeapEntry* V8HeapExplorer::AddEntry(HeapObject object, HeapEntry::Type type,
658
                                    const char* name) {
659 660 661 662
  if (FLAG_heap_profiler_show_hidden_objects && type == HeapEntry::kHidden) {
    type = HeapEntry::kNative;
  }

663
  return AddEntry(object.address(), type, name, object.Size());
664 665 666 667 668
}

HeapEntry* V8HeapExplorer::AddEntry(Address address,
                                    HeapEntry::Type type,
                                    const char* name,
669
                                    size_t size) {
670 671
  SnapshotObjectId object_id = heap_object_map_->FindOrAddEntry(
      address, static_cast<unsigned int>(size));
672 673 674 675 676 677 678
  unsigned trace_node_id = 0;
  if (AllocationTracker* allocation_tracker =
      snapshot_->profiler()->allocation_tracker()) {
    trace_node_id =
        allocation_tracker->address_to_trace()->GetTraceNodeId(address);
  }
  return snapshot_->AddEntry(type, name, object_id, size, trace_node_id);
679 680
}

681
const char* V8HeapExplorer::GetSystemEntryName(HeapObject object) {
682 683
  if (object.IsMap()) {
    switch (Map::cast(object).instance_type()) {
684 685 686 687 688
#define MAKE_STRING_MAP_CASE(instance_type, size, name, Name) \
        case instance_type: return "system / Map (" #Name ")";
      STRING_TYPE_LIST(MAKE_STRING_MAP_CASE)
#undef MAKE_STRING_MAP_CASE
        default: return "system / Map";
689 690 691 692 693 694
    }
  }

  switch (object.map().instance_type()) {
#define MAKE_TORQUE_CASE(Name, TYPE) \
  case TYPE:                         \
695
    return "system / " #Name;
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710
    // The following lists include every non-String instance type.
    // This includes a few types that already have non-"system" names assigned
    // by AddEntry, but this is a convenient way to avoid manual upkeep here.
    TORQUE_INSTANCE_CHECKERS_SINGLE_FULLY_DEFINED(MAKE_TORQUE_CASE)
    TORQUE_INSTANCE_CHECKERS_MULTIPLE_FULLY_DEFINED(MAKE_TORQUE_CASE)
    TORQUE_INSTANCE_CHECKERS_SINGLE_ONLY_DECLARED(MAKE_TORQUE_CASE)
    TORQUE_INSTANCE_CHECKERS_MULTIPLE_ONLY_DECLARED(MAKE_TORQUE_CASE)
#undef MAKE_TORQUE_CASE

    // Strings were already handled by AddEntry.
#define MAKE_STRING_CASE(instance_type, size, name, Name) \
  case instance_type:                                     \
    UNREACHABLE();
    STRING_TYPE_LIST(MAKE_STRING_CASE)
#undef MAKE_STRING_CASE
711 712 713
  }
}

714
int V8HeapExplorer::EstimateObjectsCount() {
715
  CombinedHeapObjectIterator it(heap_, HeapObjectIterator::kFilterUnreachable);
716
  int objects_count = 0;
717
  while (!it.Next().is_null()) ++objects_count;
718 719 720 721 722
  return objects_count;
}

class IndexedReferencesExtractor : public ObjectVisitor {
 public:
723
  IndexedReferencesExtractor(V8HeapExplorer* generator, HeapObject parent_obj,
724
                             HeapEntry* parent)
725 726
      : generator_(generator),
        parent_obj_(parent_obj),
727
        parent_start_(parent_obj_.RawMaybeWeakField(0)),
728
        parent_end_(parent_obj_.RawMaybeWeakField(parent_obj_.Size())),
729 730
        parent_(parent),
        next_index_(0) {}
731
  void VisitPointers(HeapObject host, ObjectSlot start,
732 733
                     ObjectSlot end) override {
    VisitPointers(host, MaybeObjectSlot(start), MaybeObjectSlot(end));
734
  }
735
  void VisitMapPointer(HeapObject object) override {
736 737 738 739
    // TODO(v8:11880): support external code space (here object could be Code,
    // so the V8 heap cage_base must be used here).
    PtrComprCageBase cage_base = GetPtrComprCageBase(object);
    VisitSlotImpl(cage_base, object.map_slot());
740
  }
741
  void VisitPointers(HeapObject host, MaybeObjectSlot start,
742
                     MaybeObjectSlot end) override {
743 744 745 746
    // [start,end) must be a sub-region of [parent_start_, parent_end), i.e.
    // all the slots must point inside the object.
    CHECK_LE(parent_start_, start);
    CHECK_LE(end, parent_end_);
747 748 749
    PtrComprCageBase cage_base = GetPtrComprCageBase(host);
    for (MaybeObjectSlot slot = start; slot < end; ++slot) {
      VisitSlotImpl(cage_base, slot);
750 751 752
    }
  }

753 754
  void VisitCodePointer(HeapObject host, CodeObjectSlot slot) override {
    CHECK(V8_EXTERNAL_CODE_SPACE_BOOL);
755 756 757
    // TODO(v8:11880): support external code space.
    PtrComprCageBase code_cage_base = GetPtrComprCageBase(host);
    VisitSlotImpl(code_cage_base, slot);
758 759
  }

760 761 762 763 764
  void VisitCodeTarget(Code host, RelocInfo* rinfo) override {
    Code target = Code::GetCodeFromTargetAddress(rinfo->target_address());
    VisitHeapObjectImpl(target, -1);
  }

765
  void VisitEmbeddedPointer(Code host, RelocInfo* rinfo) override {
766 767 768 769 770 771
    HeapObject object = rinfo->target_object();
    if (host.IsWeakObject(object)) {
      generator_->SetWeakReference(parent_, next_index_++, object, {});
    } else {
      VisitHeapObjectImpl(rinfo->target_object(), -1);
    }
772 773
  }

774
 private:
775 776 777 778 779 780 781
  template <typename TSlot>
  V8_INLINE void VisitSlotImpl(PtrComprCageBase cage_base, TSlot slot) {
    int field_index = static_cast<int>(MaybeObjectSlot(slot) - parent_start_);
    if (generator_->visited_fields_[field_index]) {
      generator_->visited_fields_[field_index] = false;
    } else {
      HeapObject heap_object;
782 783
      auto loaded_value = slot.load(cage_base);
      if (loaded_value.GetHeapObjectIfStrong(&heap_object)) {
784
        VisitHeapObjectImpl(heap_object, field_index);
785
      } else if (loaded_value.GetHeapObjectIfWeak(&heap_object)) {
786
        generator_->SetWeakReference(parent_, next_index_++, heap_object, {});
787 788 789 790
      }
    }
  }

791
  V8_INLINE void VisitHeapObjectImpl(HeapObject heap_object, int field_index) {
792 793
    DCHECK_LE(-1, field_index);
    // The last parameter {field_offset} is only used to check some well-known
794
    // skipped references, so passing -1 * kTaggedSize for objects embedded
795 796
    // into code is fine.
    generator_->SetHiddenReference(parent_obj_, parent_, next_index_++,
797
                                   heap_object, field_index * kTaggedSize);
798 799
  }

800
  V8HeapExplorer* generator_;
801
  HeapObject parent_obj_;
802 803
  MaybeObjectSlot parent_start_;
  MaybeObjectSlot parent_end_;
804
  HeapEntry* parent_;
805
  int next_index_;
806 807
};

808
void V8HeapExplorer::ExtractReferences(HeapEntry* entry, HeapObject obj) {
809
  if (obj.IsJSGlobalProxy()) {
810
    ExtractJSGlobalProxyReferences(entry, JSGlobalProxy::cast(obj));
811
  } else if (obj.IsJSArrayBuffer()) {
812
    ExtractJSArrayBufferReferences(entry, JSArrayBuffer::cast(obj));
813 814
  } else if (obj.IsJSObject()) {
    if (obj.IsJSWeakSet()) {
815
      ExtractJSWeakCollectionReferences(entry, JSWeakSet::cast(obj));
816
    } else if (obj.IsJSWeakMap()) {
817
      ExtractJSWeakCollectionReferences(entry, JSWeakMap::cast(obj));
818
    } else if (obj.IsJSSet()) {
819
      ExtractJSCollectionReferences(entry, JSSet::cast(obj));
820
    } else if (obj.IsJSMap()) {
821
      ExtractJSCollectionReferences(entry, JSMap::cast(obj));
822
    } else if (obj.IsJSPromise()) {
823
      ExtractJSPromiseReferences(entry, JSPromise::cast(obj));
824
    } else if (obj.IsJSGeneratorObject()) {
825
      ExtractJSGeneratorObjectReferences(entry, JSGeneratorObject::cast(obj));
826
    }
827
    ExtractJSObjectReferences(entry, JSObject::cast(obj));
828
  } else if (obj.IsString()) {
829
    ExtractStringReferences(entry, String::cast(obj));
830
  } else if (obj.IsSymbol()) {
831
    ExtractSymbolReferences(entry, Symbol::cast(obj));
832
  } else if (obj.IsMap()) {
833
    ExtractMapReferences(entry, Map::cast(obj));
834
  } else if (obj.IsSharedFunctionInfo()) {
835
    ExtractSharedFunctionInfoReferences(entry, SharedFunctionInfo::cast(obj));
836
  } else if (obj.IsScript()) {
837
    ExtractScriptReferences(entry, Script::cast(obj));
838
  } else if (obj.IsAccessorInfo()) {
839
    ExtractAccessorInfoReferences(entry, AccessorInfo::cast(obj));
840
  } else if (obj.IsAccessorPair()) {
841
    ExtractAccessorPairReferences(entry, AccessorPair::cast(obj));
842
  } else if (obj.IsCode()) {
843
    ExtractCodeReferences(entry, Code::cast(obj));
844
  } else if (obj.IsCell()) {
845
    ExtractCellReferences(entry, Cell::cast(obj));
846
  } else if (obj.IsFeedbackCell()) {
847
    ExtractFeedbackCellReferences(entry, FeedbackCell::cast(obj));
848
  } else if (obj.IsPropertyCell()) {
849
    ExtractPropertyCellReferences(entry, PropertyCell::cast(obj));
850
  } else if (obj.IsAllocationSite()) {
851
    ExtractAllocationSiteReferences(entry, AllocationSite::cast(obj));
852
  } else if (obj.IsArrayBoilerplateDescription()) {
853 854
    ExtractArrayBoilerplateDescriptionReferences(
        entry, ArrayBoilerplateDescription::cast(obj));
855
  } else if (obj.IsFeedbackVector()) {
856
    ExtractFeedbackVectorReferences(entry, FeedbackVector::cast(obj));
857
  } else if (obj.IsDescriptorArray()) {
858
    ExtractDescriptorArrayReferences(entry, DescriptorArray::cast(obj));
859
  } else if (obj.IsWeakFixedArray()) {
860 861
    ExtractWeakArrayReferences(WeakFixedArray::kHeaderSize, entry,
                               WeakFixedArray::cast(obj));
862
  } else if (obj.IsWeakArrayList()) {
863 864
    ExtractWeakArrayReferences(WeakArrayList::kHeaderSize, entry,
                               WeakArrayList::cast(obj));
865
  } else if (obj.IsContext()) {
866
    ExtractContextReferences(entry, Context::cast(obj));
867
  } else if (obj.IsEphemeronHashTable()) {
868
    ExtractEphemeronHashTableReferences(entry, EphemeronHashTable::cast(obj));
869
  } else if (obj.IsFixedArray()) {
870
    ExtractFixedArrayReferences(entry, FixedArray::cast(obj));
Arno Renevier's avatar
Arno Renevier committed
871 872 873 874
  } else if (obj.IsHeapNumber()) {
    if (snapshot_->capture_numeric_value()) {
      ExtractNumberReference(entry, obj);
    }
875
  }
876 877
}

878
void V8HeapExplorer::ExtractJSGlobalProxyReferences(HeapEntry* entry,
879
                                                    JSGlobalProxy proxy) {
880
  SetInternalReference(entry, "native_context", proxy.native_context(),
881
                       JSGlobalProxy::kNativeContextOffset);
882 883
}

884
void V8HeapExplorer::ExtractJSObjectReferences(HeapEntry* entry,
885
                                               JSObject js_obj) {
886
  HeapObject obj = js_obj;
887 888 889
  ExtractPropertyReferences(js_obj, entry);
  ExtractElementReferences(js_obj, entry);
  ExtractInternalReferences(js_obj, entry);
890 891 892
  Isolate* isolate = Isolate::FromHeap(heap_);
  PrototypeIterator iter(isolate, js_obj);
  ReadOnlyRoots roots(isolate);
893
  SetPropertyReference(entry, roots.proto_string(), iter.GetCurrent());
894
  if (obj.IsJSBoundFunction()) {
895
    JSBoundFunction js_fun = JSBoundFunction::cast(obj);
896 897
    TagObject(js_fun.bound_arguments(), "(bound arguments)");
    SetInternalReference(entry, "bindings", js_fun.bound_arguments(),
898
                         JSBoundFunction::kBoundArgumentsOffset);
899
    SetInternalReference(entry, "bound_this", js_fun.bound_this(),
900
                         JSBoundFunction::kBoundThisOffset);
901
    SetInternalReference(entry, "bound_function",
902
                         js_fun.bound_target_function(),
903
                         JSBoundFunction::kBoundTargetFunctionOffset);
904 905
    FixedArray bindings = js_fun.bound_arguments();
    for (int i = 0; i < bindings.length(); i++) {
906
      const char* reference_name = names_->GetFormatted("bound_argument_%d", i);
907
      SetNativeBindReference(entry, reference_name, bindings.get(i));
908
    }
909
  } else if (obj.IsJSFunction()) {
910
    JSFunction js_fun = JSFunction::cast(js_obj);
911
    if (js_fun.has_prototype_slot()) {
912
      Object proto_or_map = js_fun.prototype_or_initial_map(kAcquireLoad);
913 914
      if (!proto_or_map.IsTheHole(isolate)) {
        if (!proto_or_map.IsMap()) {
915 916
          SetPropertyReference(entry, roots.prototype_string(), proto_or_map,
                               nullptr,
917 918
                               JSFunction::kPrototypeOrInitialMapOffset);
        } else {
919
          SetPropertyReference(entry, roots.prototype_string(),
920
                               js_fun.prototype());
921
          SetInternalReference(entry, "initial_map", proto_or_map,
922 923
                               JSFunction::kPrototypeOrInitialMapOffset);
        }
924 925
      }
    }
926 927 928
    SharedFunctionInfo shared_info = js_fun.shared();
    TagObject(js_fun.raw_feedback_cell(), "(function feedback cell)");
    SetInternalReference(entry, "feedback_cell", js_fun.raw_feedback_cell(),
929
                         JSFunction::kFeedbackCellOffset);
930
    TagObject(shared_info, "(shared function info)");
931
    SetInternalReference(entry, "shared", shared_info,
932
                         JSFunction::kSharedFunctionInfoOffset);
933 934
    TagObject(js_fun.context(), "(context)");
    SetInternalReference(entry, "context", js_fun.context(),
935
                         JSFunction::kContextOffset);
936 937
    SetInternalReference(entry, "code", js_fun.code(), JSFunction::kCodeOffset);
  } else if (obj.IsJSGlobalObject()) {
938
    JSGlobalObject global_obj = JSGlobalObject::cast(obj);
939
    SetInternalReference(entry, "native_context", global_obj.native_context(),
940
                         JSGlobalObject::kNativeContextOffset);
941
    SetInternalReference(entry, "global_proxy", global_obj.global_proxy(),
942
                         JSGlobalObject::kGlobalProxyOffset);
943
    STATIC_ASSERT(JSGlobalObject::kHeaderSize - JSObject::kHeaderSize ==
944
                  2 * kTaggedSize);
945
  } else if (obj.IsJSArrayBufferView()) {
946
    JSArrayBufferView view = JSArrayBufferView::cast(obj);
947
    SetInternalReference(entry, "buffer", view.buffer(),
948
                         JSArrayBufferView::kBufferOffset);
949
  }
950

951 952
  TagObject(js_obj.raw_properties_or_hash(), "(object properties)");
  SetInternalReference(entry, "properties", js_obj.raw_properties_or_hash(),
953
                       JSObject::kPropertiesOrHashOffset);
954

955 956
  TagObject(js_obj.elements(), "(object elements)");
  SetInternalReference(entry, "elements", js_obj.elements(),
957 958 959
                       JSObject::kElementsOffset);
}

960
void V8HeapExplorer::ExtractStringReferences(HeapEntry* entry, String string) {
961
  if (string.IsConsString()) {
962
    ConsString cs = ConsString::cast(string);
963 964
    SetInternalReference(entry, "first", cs.first(), ConsString::kFirstOffset);
    SetInternalReference(entry, "second", cs.second(),
965
                         ConsString::kSecondOffset);
966
  } else if (string.IsSlicedString()) {
967
    SlicedString ss = SlicedString::cast(string);
968
    SetInternalReference(entry, "parent", ss.parent(),
969
                         SlicedString::kParentOffset);
970
  } else if (string.IsThinString()) {
971
    ThinString ts = ThinString::cast(string);
972
    SetInternalReference(entry, "actual", ts.actual(),
973
                         ThinString::kActualOffset);
974 975 976
  }
}

977
void V8HeapExplorer::ExtractSymbolReferences(HeapEntry* entry, Symbol symbol) {
978 979
  SetInternalReference(entry, "name", symbol.description(),
                       Symbol::kDescriptionOffset);
980 981
}

982
void V8HeapExplorer::ExtractJSCollectionReferences(HeapEntry* entry,
983
                                                   JSCollection collection) {
984
  SetInternalReference(entry, "table", collection.table(),
985 986 987
                       JSCollection::kTableOffset);
}

988
void V8HeapExplorer::ExtractJSWeakCollectionReferences(HeapEntry* entry,
989
                                                       JSWeakCollection obj) {
990
  SetInternalReference(entry, "table", obj.table(),
991 992 993
                       JSWeakCollection::kTableOffset);
}

994
void V8HeapExplorer::ExtractEphemeronHashTableReferences(
995
    HeapEntry* entry, EphemeronHashTable table) {
996
  for (InternalIndex i : table.IterateEntries()) {
997 998 999
    int key_index = EphemeronHashTable::EntryToIndex(i) +
                    EphemeronHashTable::kEntryKeyIndex;
    int value_index = EphemeronHashTable::EntryToValueIndex(i);
1000 1001 1002
    Object key = table.get(key_index);
    Object value = table.get(value_index);
    SetWeakReference(entry, key_index, key, table.OffsetOfElementAt(key_index));
1003
    SetWeakReference(entry, value_index, value,
1004
                     table.OffsetOfElementAt(value_index));
1005 1006
    HeapEntry* key_entry = GetEntry(key);
    HeapEntry* value_entry = GetEntry(value);
1007 1008 1009 1010 1011 1012
    HeapEntry* table_entry = GetEntry(table);
    if (key_entry && value_entry && !key.IsUndefined()) {
      const char* edge_name = names_->GetFormatted(
          "part of key (%s @%u) -> value (%s @%u) pair in WeakMap (table @%u)",
          key_entry->name(), key_entry->id(), value_entry->name(),
          value_entry->id(), table_entry->id());
1013 1014
      key_entry->SetNamedAutoIndexReference(HeapGraphEdge::kInternal, edge_name,
                                            value_entry, names_);
1015 1016
      table_entry->SetNamedAutoIndexReference(HeapGraphEdge::kInternal,
                                              edge_name, value_entry, names_);
1017 1018 1019 1020
    }
  }
}

1021 1022 1023
// These static arrays are used to prevent excessive code-size in
// ExtractContextReferences below, which would happen if we called
// SetInternalReference for every native context field in a macro.
1024 1025 1026 1027 1028 1029
static const struct {
  int index;
  const char* name;
} native_context_names[] = {
#define CONTEXT_FIELD_INDEX_NAME(index, _, name) {Context::index, #name},
    NATIVE_CONTEXT_FIELDS(CONTEXT_FIELD_INDEX_NAME)
1030
#undef CONTEXT_FIELD_INDEX_NAME
1031 1032
};

1033
void V8HeapExplorer::ExtractContextReferences(HeapEntry* entry,
1034
                                              Context context) {
1035 1036
  if (!context.IsNativeContext() && context.is_declaration_context()) {
    ScopeInfo scope_info = context.scope_info();
1037
    // Add context allocated locals.
1038
    int context_locals = scope_info.ContextLocalCount();
1039
    for (int i = 0; i < context_locals; ++i) {
1040
      String local_name = scope_info.ContextLocalName(i);
1041
      int idx = scope_info.ContextHeaderLength() + i;
1042
      SetContextReference(entry, local_name, context.get(idx),
1043 1044
                          Context::OffsetOfElementAt(idx));
    }
1045 1046 1047
    if (scope_info.HasFunctionName()) {
      String name = String::cast(scope_info.FunctionName());
      int idx = scope_info.FunctionContextSlotIndex(name);
1048
      if (idx >= 0) {
1049
        SetContextReference(entry, name, context.get(idx),
1050 1051 1052 1053 1054
                            Context::OffsetOfElementAt(idx));
      }
    }
  }

1055
  SetInternalReference(
1056
      entry, "scope_info", context.get(Context::SCOPE_INFO_INDEX),
1057
      FixedArray::OffsetOfElementAt(Context::SCOPE_INFO_INDEX));
1058
  SetInternalReference(entry, "previous", context.get(Context::PREVIOUS_INDEX),
1059
                       FixedArray::OffsetOfElementAt(Context::PREVIOUS_INDEX));
1060 1061 1062 1063 1064
  if (context.has_extension()) {
    SetInternalReference(
        entry, "extension", context.get(Context::EXTENSION_INDEX),
        FixedArray::OffsetOfElementAt(Context::EXTENSION_INDEX));
  }
1065

1066 1067 1068
  if (context.IsNativeContext()) {
    TagObject(context.normalized_map_cache(), "(context norm. map cache)");
    TagObject(context.embedder_data(), "(context data)");
1069 1070 1071
    for (size_t i = 0; i < arraysize(native_context_names); i++) {
      int index = native_context_names[i].index;
      const char* name = native_context_names[i].name;
1072
      SetInternalReference(entry, name, context.get(index),
1073 1074 1075
                           FixedArray::OffsetOfElementAt(index));
    }

1076 1077 1078
    SetWeakReference(entry, "optimized_code_list",
                     context.get(Context::OPTIMIZED_CODE_LIST),
                     Context::OffsetOfElementAt(Context::OPTIMIZED_CODE_LIST));
1079
    SetWeakReference(
1080
        entry, "deoptimized_code_list",
1081
        context.get(Context::DEOPTIMIZED_CODE_LIST),
1082
        Context::OffsetOfElementAt(Context::DEOPTIMIZED_CODE_LIST));
1083
    STATIC_ASSERT(Context::OPTIMIZED_CODE_LIST == Context::FIRST_WEAK_SLOT);
1084 1085
    STATIC_ASSERT(Context::NEXT_CONTEXT_LINK + 1 ==
                  Context::NATIVE_CONTEXT_SLOTS);
1086
    STATIC_ASSERT(Context::FIRST_WEAK_SLOT + 3 ==
1087
                  Context::NATIVE_CONTEXT_SLOTS);
1088 1089 1090
  }
}

1091
void V8HeapExplorer::ExtractMapReferences(HeapEntry* entry, Map map) {
1092
  MaybeObject maybe_raw_transitions_or_prototype_info = map.raw_transitions();
1093
  HeapObject raw_transitions_or_prototype_info;
1094
  if (maybe_raw_transitions_or_prototype_info->GetHeapObjectIfWeak(
1095
          &raw_transitions_or_prototype_info)) {
1096
    DCHECK(raw_transitions_or_prototype_info.IsMap());
1097
    SetWeakReference(entry, "transition", raw_transitions_or_prototype_info,
1098
                     Map::kTransitionsOrPrototypeInfoOffset);
1099
  } else if (maybe_raw_transitions_or_prototype_info->GetHeapObjectIfStrong(
1100
                 &raw_transitions_or_prototype_info)) {
1101
    if (raw_transitions_or_prototype_info.IsTransitionArray()) {
1102
      TransitionArray transitions =
1103
          TransitionArray::cast(raw_transitions_or_prototype_info);
1104 1105
      if (map.CanTransition() && transitions.HasPrototypeTransitions()) {
        TagObject(transitions.GetPrototypeTransitions(),
1106 1107 1108
                  "(prototype transitions)");
      }
      TagObject(transitions, "(transition array)");
1109
      SetInternalReference(entry, "transitions", transitions,
1110
                           Map::kTransitionsOrPrototypeInfoOffset);
1111
    } else if (raw_transitions_or_prototype_info.IsFixedArray()) {
1112
      TagObject(raw_transitions_or_prototype_info, "(transition)");
1113
      SetInternalReference(entry, "transition",
1114 1115
                           raw_transitions_or_prototype_info,
                           Map::kTransitionsOrPrototypeInfoOffset);
1116
    } else if (map.is_prototype_map()) {
1117
      TagObject(raw_transitions_or_prototype_info, "prototype_info");
1118
      SetInternalReference(entry, "prototype_info",
1119 1120
                           raw_transitions_or_prototype_info,
                           Map::kTransitionsOrPrototypeInfoOffset);
1121
    }
1122
  }
1123
  DescriptorArray descriptors = map.instance_descriptors();
1124
  TagObject(descriptors, "(map descriptors)");
1125
  SetInternalReference(entry, "descriptors", descriptors,
1126
                       Map::kInstanceDescriptorsOffset);
1127
  SetInternalReference(entry, "prototype", map.prototype(),
1128
                       Map::kPrototypeOffset);
1129 1130 1131 1132 1133
  if (map.IsContextMap()) {
    Object native_context = map.native_context();
    TagObject(native_context, "(native context)");
    SetInternalReference(entry, "native_context", native_context,
                         Map::kConstructorOrBackPointerOrNativeContextOffset);
1134
  } else {
1135 1136 1137 1138
    Object constructor_or_back_pointer = map.constructor_or_back_pointer();
    if (constructor_or_back_pointer.IsMap()) {
      TagObject(constructor_or_back_pointer, "(back pointer)");
      SetInternalReference(entry, "back_pointer", constructor_or_back_pointer,
1139
                           Map::kConstructorOrBackPointerOrNativeContextOffset);
1140 1141
    } else if (constructor_or_back_pointer.IsFunctionTemplateInfo()) {
      TagObject(constructor_or_back_pointer, "(constructor function data)");
1142
      SetInternalReference(entry, "constructor_function_data",
1143
                           constructor_or_back_pointer,
1144 1145
                           Map::kConstructorOrBackPointerOrNativeContextOffset);
    } else {
1146
      SetInternalReference(entry, "constructor", constructor_or_back_pointer,
1147 1148
                           Map::kConstructorOrBackPointerOrNativeContextOffset);
    }
1149
  }
1150 1151
  TagObject(map.dependent_code(), "(dependent code)");
  SetInternalReference(entry, "dependent_code", map.dependent_code(),
1152
                       Map::kDependentCodeOffset);
1153 1154 1155
}

void V8HeapExplorer::ExtractSharedFunctionInfoReferences(
1156
    HeapEntry* entry, SharedFunctionInfo shared) {
1157 1158 1159 1160
  std::unique_ptr<char[]> name = shared.DebugNameCStr();
  if (name[0] != '\0') {
    TagObject(shared.GetCode(),
              names_->GetFormatted("(code for %s)", name.get()));
1161
  } else {
1162 1163
    TagObject(shared.GetCode(),
              names_->GetFormatted("(%s code)",
1164
                                   CodeKindToString(shared.GetCode().kind())));
1165 1166
  }

1167 1168 1169
  Object name_or_scope_info = shared.name_or_scope_info(kAcquireLoad);
  if (name_or_scope_info.IsScopeInfo()) {
    TagObject(name_or_scope_info, "(function scope info)");
1170
  }
1171
  SetInternalReference(entry, "name_or_scope_info", name_or_scope_info,
1172
                       SharedFunctionInfo::kNameOrScopeInfoOffset);
1173
  SetInternalReference(entry, "script_or_debug_info",
1174
                       shared.script_or_debug_info(kAcquireLoad),
1175
                       SharedFunctionInfo::kScriptOrDebugInfoOffset);
1176 1177
  SetInternalReference(entry, "function_data",
                       shared.function_data(kAcquireLoad),
1178
                       SharedFunctionInfo::kFunctionDataOffset);
1179
  SetInternalReference(
1180
      entry, "raw_outer_scope_info_or_feedback_metadata",
1181
      shared.raw_outer_scope_info_or_feedback_metadata(),
1182
      SharedFunctionInfo::kOuterScopeInfoOrFeedbackMetadataOffset);
1183 1184
}

1185
void V8HeapExplorer::ExtractScriptReferences(HeapEntry* entry, Script script) {
1186 1187 1188
  SetInternalReference(entry, "source", script.source(), Script::kSourceOffset);
  SetInternalReference(entry, "name", script.name(), Script::kNameOffset);
  SetInternalReference(entry, "context_data", script.context_data(),
1189
                       Script::kContextDataOffset);
1190 1191
  TagObject(script.line_ends(), "(script line ends)");
  SetInternalReference(entry, "line_ends", script.line_ends(),
1192 1193 1194
                       Script::kLineEndsOffset);
}

1195 1196
void V8HeapExplorer::ExtractAccessorInfoReferences(HeapEntry* entry,
                                                   AccessorInfo accessor_info) {
1197
  SetInternalReference(entry, "name", accessor_info.name(),
1198
                       AccessorInfo::kNameOffset);
1199
  SetInternalReference(entry, "expected_receiver_type",
1200
                       accessor_info.expected_receiver_type(),
1201
                       AccessorInfo::kExpectedReceiverTypeOffset);
1202
  SetInternalReference(entry, "getter", accessor_info.getter(),
1203
                       AccessorInfo::kGetterOffset);
1204
  SetInternalReference(entry, "setter", accessor_info.setter(),
1205
                       AccessorInfo::kSetterOffset);
1206
  SetInternalReference(entry, "data", accessor_info.data(),
1207
                       AccessorInfo::kDataOffset);
1208 1209
}

1210
void V8HeapExplorer::ExtractAccessorPairReferences(HeapEntry* entry,
1211
                                                   AccessorPair accessors) {
1212
  SetInternalReference(entry, "getter", accessors.getter(),
1213
                       AccessorPair::kGetterOffset);
1214
  SetInternalReference(entry, "setter", accessors.setter(),
1215 1216 1217
                       AccessorPair::kSetterOffset);
}

1218
void V8HeapExplorer::TagBuiltinCodeObject(Code code, const char* name) {
1219
  TagObject(code, names_->GetFormatted("(%s builtin)", name));
1220 1221
}

1222
void V8HeapExplorer::ExtractCodeReferences(HeapEntry* entry, Code code) {
1223 1224 1225
  Object reloc_info_or_undefined = code.relocation_info_or_undefined();
  TagObject(reloc_info_or_undefined, "(code relocation info)");
  SetInternalReference(entry, "relocation_info", reloc_info_or_undefined,
1226
                       Code::kRelocationInfoOffset);
1227 1228 1229 1230 1231 1232 1233
  if (reloc_info_or_undefined.IsUndefined()) {
    // The code object was compiled directly on the heap, but it was not
    // finalized.
    DCHECK(code.kind() == CodeKind::BASELINE);
    return;
  }

1234
  if (code.kind() == CodeKind::BASELINE) {
1235 1236 1237 1238
    TagObject(code.bytecode_or_interpreter_data(), "(interpreter data)");
    SetInternalReference(entry, "interpreter_data",
                         code.bytecode_or_interpreter_data(),
                         Code::kDeoptimizationDataOrInterpreterDataOffset);
1239 1240 1241 1242 1243
    TagObject(code.bytecode_offset_table(), "(bytecode offset table)");
    SetInternalReference(entry, "bytecode_offset_table",
                         code.bytecode_offset_table(),
                         Code::kPositionTableOffset);
  } else {
1244 1245 1246 1247
    TagObject(code.deoptimization_data(), "(code deopt data)");
    SetInternalReference(entry, "deoptimization_data",
                         code.deoptimization_data(),
                         Code::kDeoptimizationDataOrInterpreterDataOffset);
1248 1249 1250 1251 1252
    TagObject(code.source_position_table(), "(source position table)");
    SetInternalReference(entry, "source_position_table",
                         code.source_position_table(),
                         Code::kPositionTableOffset);
  }
1253 1254
}

1255
void V8HeapExplorer::ExtractCellReferences(HeapEntry* entry, Cell cell) {
1256
  SetInternalReference(entry, "value", cell.value(), Cell::kValueOffset);
1257 1258
}

1259 1260
void V8HeapExplorer::ExtractFeedbackCellReferences(HeapEntry* entry,
                                                   FeedbackCell feedback_cell) {
1261
  TagObject(feedback_cell, "(feedback cell)");
1262
  SetInternalReference(entry, "value", feedback_cell.value(),
1263 1264 1265
                       FeedbackCell::kValueOffset);
}

1266
void V8HeapExplorer::ExtractPropertyCellReferences(HeapEntry* entry,
1267
                                                   PropertyCell cell) {
1268
  SetInternalReference(entry, "value", cell.value(),
1269
                       PropertyCell::kValueOffset);
1270 1271
  TagObject(cell.dependent_code(), "(dependent code)");
  SetInternalReference(entry, "dependent_code", cell.dependent_code(),
1272
                       PropertyCell::kDependentCodeOffset);
1273 1274
}

1275
void V8HeapExplorer::ExtractAllocationSiteReferences(HeapEntry* entry,
1276
                                                     AllocationSite site) {
1277
  SetInternalReference(entry, "transition_info",
1278
                       site.transition_info_or_boilerplate(),
1279
                       AllocationSite::kTransitionInfoOrBoilerplateOffset);
1280
  SetInternalReference(entry, "nested_site", site.nested_site(),
1281
                       AllocationSite::kNestedSiteOffset);
1282 1283
  TagObject(site.dependent_code(), "(dependent code)");
  SetInternalReference(entry, "dependent_code", site.dependent_code(),
1284
                       AllocationSite::kDependentCodeOffset);
1285 1286
}

1287
void V8HeapExplorer::ExtractArrayBoilerplateDescriptionReferences(
1288
    HeapEntry* entry, ArrayBoilerplateDescription value) {
1289
  SetInternalReference(entry, "constant_elements", value.constant_elements(),
1290 1291 1292
                       ArrayBoilerplateDescription::kConstantElementsOffset);
}

1293 1294
class JSArrayBufferDataEntryAllocator : public HeapEntriesAllocator {
 public:
1295
  JSArrayBufferDataEntryAllocator(size_t size, V8HeapExplorer* explorer)
1296 1297 1298
      : size_(size)
      , explorer_(explorer) {
  }
1299
  HeapEntry* AllocateEntry(HeapThing ptr) override {
1300 1301 1302
    return explorer_->AddEntry(reinterpret_cast<Address>(ptr),
                               HeapEntry::kNative, "system / JSArrayBufferData",
                               size_);
1303
  }
Arno Renevier's avatar
Arno Renevier committed
1304 1305 1306 1307 1308
  HeapEntry* AllocateEntry(Smi smi) override {
    DCHECK(false);
    return nullptr;
  }

1309
 private:
1310
  size_t size_;
1311 1312 1313
  V8HeapExplorer* explorer_;
};

1314
void V8HeapExplorer::ExtractJSArrayBufferReferences(HeapEntry* entry,
1315
                                                    JSArrayBuffer buffer) {
1316
  // Setup a reference to a native memory backing_store object.
1317 1318
  if (!buffer.backing_store()) return;
  size_t data_size = buffer.byte_length();
1319
  JSArrayBufferDataEntryAllocator allocator(data_size, this);
1320
  HeapEntry* data_entry =
1321
      generator_->FindOrAddEntry(buffer.backing_store(), &allocator);
1322 1323
  entry->SetNamedReference(HeapGraphEdge::kInternal, "backing_store",
                           data_entry);
1324 1325
}

1326
void V8HeapExplorer::ExtractJSPromiseReferences(HeapEntry* entry,
1327
                                                JSPromise promise) {
1328
  SetInternalReference(entry, "reactions_or_result",
1329
                       promise.reactions_or_result(),
1330
                       JSPromise::kReactionsOrResultOffset);
1331 1332
}

1333
void V8HeapExplorer::ExtractJSGeneratorObjectReferences(
1334
    HeapEntry* entry, JSGeneratorObject generator) {
1335
  SetInternalReference(entry, "function", generator.function(),
1336
                       JSGeneratorObject::kFunctionOffset);
1337
  SetInternalReference(entry, "context", generator.context(),
1338
                       JSGeneratorObject::kContextOffset);
1339
  SetInternalReference(entry, "receiver", generator.receiver(),
1340
                       JSGeneratorObject::kReceiverOffset);
1341
  SetInternalReference(entry, "parameters_and_registers",
1342
                       generator.parameters_and_registers(),
1343 1344 1345
                       JSGeneratorObject::kParametersAndRegistersOffset);
}

1346
void V8HeapExplorer::ExtractFixedArrayReferences(HeapEntry* entry,
1347
                                                 FixedArray array) {
1348 1349 1350
  for (int i = 0, l = array.length(); i < l; ++i) {
    DCHECK(!HasWeakHeapObjectTag(array.get(i)));
    SetInternalReference(entry, i, array.get(i), array.OffsetOfElementAt(i));
1351 1352
  }
}
1353

Arno Renevier's avatar
Arno Renevier committed
1354 1355 1356 1357 1358
void V8HeapExplorer::ExtractNumberReference(HeapEntry* entry, Object number) {
  DCHECK(number.IsNumber());

  // Must be large enough to fit any double, int, or size_t.
  char arr[32];
1359
  base::Vector<char> buffer(arr, arraysize(arr));
Arno Renevier's avatar
Arno Renevier committed
1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377

  const char* string;
  if (number.IsSmi()) {
    int int_value = Smi::ToInt(number);
    string = IntToCString(int_value, buffer);
  } else {
    double double_value = HeapNumber::cast(number).value();
    string = DoubleToCString(double_value, buffer);
  }

  const char* name = names_->GetCopy(string);

  SnapshotObjectId id = heap_object_map_->get_next_id();
  HeapEntry* child_entry =
      snapshot_->AddEntry(HeapEntry::kString, name, id, 0, 0);
  entry->SetNamedReference(HeapGraphEdge::kInternal, "value", child_entry);
}

1378
void V8HeapExplorer::ExtractFeedbackVectorReferences(
1379
    HeapEntry* entry, FeedbackVector feedback_vector) {
1380
  MaybeObject code = feedback_vector.maybe_optimized_code();
1381
  HeapObject code_heap_object;
1382
  if (code->GetHeapObjectIfWeak(&code_heap_object)) {
1383
    SetWeakReference(entry, "optimized code", code_heap_object,
1384
                     FeedbackVector::kMaybeOptimizedCodeOffset);
1385 1386 1387
  }
}

1388
void V8HeapExplorer::ExtractDescriptorArrayReferences(HeapEntry* entry,
1389
                                                      DescriptorArray array) {
1390
  SetInternalReference(entry, "enum_cache", array.enum_cache(),
1391
                       DescriptorArray::kEnumCacheOffset);
1392
  MaybeObjectSlot start = MaybeObjectSlot(array.GetDescriptorSlot(0));
1393
  MaybeObjectSlot end = MaybeObjectSlot(
1394
      array.GetDescriptorSlot(array.number_of_all_descriptors()));
1395 1396
  for (int i = 0; start + i < end; ++i) {
    MaybeObjectSlot slot = start + i;
1397
    int offset = static_cast<int>(slot.address() - array.address());
1398
    MaybeObject object = *slot;
1399
    HeapObject heap_object;
1400 1401 1402 1403 1404 1405 1406 1407
    if (object->GetHeapObjectIfWeak(&heap_object)) {
      SetWeakReference(entry, i, heap_object, offset);
    } else if (object->GetHeapObjectIfStrong(&heap_object)) {
      SetInternalReference(entry, i, heap_object, offset);
    }
  }
}

1408
template <typename T>
1409
void V8HeapExplorer::ExtractWeakArrayReferences(int header_size,
1410
                                                HeapEntry* entry, T array) {
1411 1412
  for (int i = 0; i < array.length(); ++i) {
    MaybeObject object = array.Get(i);
1413
    HeapObject heap_object;
1414
    if (object->GetHeapObjectIfWeak(&heap_object)) {
1415
      SetWeakReference(entry, i, heap_object, header_size + i * kTaggedSize);
1416
    } else if (object->GetHeapObjectIfStrong(&heap_object)) {
1417
      SetInternalReference(entry, i, heap_object,
1418
                           header_size + i * kTaggedSize);
1419 1420 1421 1422
    }
  }
}

1423
void V8HeapExplorer::ExtractPropertyReferences(JSObject js_obj,
1424
                                               HeapEntry* entry) {
1425 1426
  Isolate* isolate = js_obj.GetIsolate();
  if (js_obj.HasFastProperties()) {
1427
    DescriptorArray descs = js_obj.map().instance_descriptors(isolate);
1428
    for (InternalIndex i : js_obj.map().IterateOwnDescriptors()) {
1429
      PropertyDetails details = descs.GetDetails(i);
1430
      switch (details.location()) {
1431
        case kField: {
Arno Renevier's avatar
Arno Renevier committed
1432 1433 1434 1435
          if (!snapshot_->capture_numeric_value()) {
            Representation r = details.representation();
            if (r.IsSmi() || r.IsDouble()) break;
          }
1436

1437 1438 1439
          Name k = descs.GetKey(i);
          FieldIndex field_index = FieldIndex::ForDescriptor(js_obj.map(), i);
          Object value = js_obj.RawFastPropertyAt(field_index);
1440 1441 1442
          int field_offset =
              field_index.is_inobject() ? field_index.offset() : -1;

1443 1444
          SetDataOrAccessorPropertyReference(details.kind(), entry, k, value,
                                             nullptr, field_offset);
1445 1446
          break;
        }
1447
        case kDescriptor:
1448 1449
          SetDataOrAccessorPropertyReference(
              details.kind(), entry, descs.GetKey(i), descs.GetStrongValue(i));
1450 1451 1452
          break;
      }
    }
1453
  } else if (js_obj.IsJSGlobalObject()) {
1454
    // We assume that global objects can only have slow properties.
1455
    GlobalDictionary dictionary =
1456
        JSGlobalObject::cast(js_obj).global_dictionary(kAcquireLoad);
1457
    ReadOnlyRoots roots(isolate);
1458
    for (InternalIndex i : dictionary.IterateEntries()) {
1459 1460 1461 1462 1463
      if (!dictionary.IsKey(roots, dictionary.KeyAt(i))) continue;
      PropertyCell cell = dictionary.CellAt(i);
      Name name = cell.name();
      Object value = cell.value();
      PropertyDetails details = cell.property_details();
1464
      SetDataOrAccessorPropertyReference(details.kind(), entry, name, value);
1465
    }
1466
  } else if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
1467 1468 1469 1470
    // SwissNameDictionary::IterateEntries creates a Handle, which should not
    // leak out of here.
    HandleScope scope(isolate);

1471
    SwissNameDictionary dictionary = js_obj.property_dictionary_swiss();
1472 1473 1474 1475 1476 1477 1478 1479 1480
    ReadOnlyRoots roots(isolate);
    for (InternalIndex i : dictionary.IterateEntries()) {
      Object k = dictionary.KeyAt(i);
      if (!dictionary.IsKey(roots, k)) continue;
      Object value = dictionary.ValueAt(i);
      PropertyDetails details = dictionary.DetailsAt(i);
      SetDataOrAccessorPropertyReference(details.kind(), entry, Name::cast(k),
                                         value);
    }
1481
  } else {
1482
    NameDictionary dictionary = js_obj.property_dictionary();
1483
    ReadOnlyRoots roots(isolate);
1484
    for (InternalIndex i : dictionary.IterateEntries()) {
1485 1486 1487 1488
      Object k = dictionary.KeyAt(i);
      if (!dictionary.IsKey(roots, k)) continue;
      Object value = dictionary.ValueAt(i);
      PropertyDetails details = dictionary.DetailsAt(i);
1489 1490
      SetDataOrAccessorPropertyReference(details.kind(), entry, Name::cast(k),
                                         value);
1491 1492 1493 1494
    }
  }
}

1495
void V8HeapExplorer::ExtractAccessorPairProperty(HeapEntry* entry, Name key,
1496
                                                 Object callback_obj,
1497
                                                 int field_offset) {
1498
  if (!callback_obj.IsAccessorPair()) return;
1499
  AccessorPair accessors = AccessorPair::cast(callback_obj);
1500
  SetPropertyReference(entry, key, accessors, nullptr, field_offset);
1501 1502
  Object getter = accessors.getter();
  if (!getter.IsOddball()) {
1503
    SetPropertyReference(entry, key, getter, "get %s");
1504
  }
1505 1506
  Object setter = accessors.setter();
  if (!setter.IsOddball()) {
1507
    SetPropertyReference(entry, key, setter, "set %s");
1508 1509 1510
  }
}

1511
void V8HeapExplorer::ExtractElementReferences(JSObject js_obj,
1512
                                              HeapEntry* entry) {
1513 1514 1515 1516 1517
  ReadOnlyRoots roots = js_obj.GetReadOnlyRoots();
  if (js_obj.HasObjectElements()) {
    FixedArray elements = FixedArray::cast(js_obj.elements());
    int length = js_obj.IsJSArray() ? Smi::ToInt(JSArray::cast(js_obj).length())
                                    : elements.length();
1518
    for (int i = 0; i < length; ++i) {
1519 1520
      if (!elements.get(i).IsTheHole(roots)) {
        SetElementReference(entry, i, elements.get(i));
1521 1522
      }
    }
1523 1524
  } else if (js_obj.HasDictionaryElements()) {
    NumberDictionary dictionary = js_obj.element_dictionary();
1525
    for (InternalIndex i : dictionary.IterateEntries()) {
1526 1527 1528 1529 1530
      Object k = dictionary.KeyAt(i);
      if (!dictionary.IsKey(roots, k)) continue;
      DCHECK(k.IsNumber());
      uint32_t index = static_cast<uint32_t>(k.Number());
      SetElementReference(entry, index, dictionary.ValueAt(i));
1531 1532 1533 1534
    }
  }
}

1535
void V8HeapExplorer::ExtractInternalReferences(JSObject js_obj,
1536
                                               HeapEntry* entry) {
1537
  int length = js_obj.GetEmbedderFieldCount();
1538
  for (int i = 0; i < length; ++i) {
1539 1540
    Object o = js_obj.GetEmbedderField(i);
    SetInternalReference(entry, i, o, js_obj.GetEmbedderFieldOffset(i));
1541 1542 1543
  }
}

1544
JSFunction V8HeapExplorer::GetConstructor(JSReceiver receiver) {
1545
  Isolate* isolate = receiver.GetIsolate();
1546
  DisallowGarbageCollection no_gc;
1547 1548 1549 1550
  HandleScope scope(isolate);
  MaybeHandle<JSFunction> maybe_constructor =
      JSReceiver::GetConstructor(handle(receiver, isolate));

1551
  if (maybe_constructor.is_null()) return JSFunction();
1552 1553 1554

  return *maybe_constructor.ToHandleChecked();
}
1555

1556
String V8HeapExplorer::GetConstructorName(JSObject object) {
1557 1558
  Isolate* isolate = object.GetIsolate();
  if (object.IsJSFunction()) return ReadOnlyRoots(isolate).closure_string();
1559
  DisallowGarbageCollection no_gc;
1560 1561
  HandleScope scope(isolate);
  return *JSReceiver::GetConstructorName(handle(object, isolate));
1562 1563
}

1564
HeapEntry* V8HeapExplorer::GetEntry(Object obj) {
Arno Renevier's avatar
Arno Renevier committed
1565 1566 1567 1568 1569 1570 1571 1572 1573
  if (obj.IsHeapObject()) {
    return generator_->FindOrAddEntry(reinterpret_cast<void*>(obj.ptr()), this);
  }

  DCHECK(obj.IsSmi());
  if (!snapshot_->capture_numeric_value()) {
    return nullptr;
  }
  return generator_->FindOrAddEntry(Smi::cast(obj), this);
1574 1575
}

1576
class RootsReferencesExtractor : public RootVisitor {
1577
 public:
1578 1579
  explicit RootsReferencesExtractor(V8HeapExplorer* explorer)
      : explorer_(explorer), visiting_weak_roots_(false) {}
1580

1581
  void SetVisitingWeakRoots() { visiting_weak_roots_ = true; }
1582

1583
  void VisitRootPointer(Root root, const char* description,
1584
                        FullObjectSlot object) override {
1585 1586
    if (root == Root::kBuiltins) {
      explorer_->TagBuiltinCodeObject(Code::cast(*object), description);
1587
    }
1588 1589
    explorer_->SetGcSubrootReference(root, description, visiting_weak_roots_,
                                     *object);
1590 1591
  }

1592 1593 1594
  void VisitRootPointers(Root root, const char* description,
                         FullObjectSlot start, FullObjectSlot end) override {
    for (FullObjectSlot p = start; p < end; ++p) {
1595
      DCHECK(!MapWord::IsPacked(p.Relaxed_Load().ptr()));
1596
      VisitRootPointer(root, description, p);
1597
    }
1598 1599
  }

1600 1601 1602 1603
  void VisitRootPointers(Root root, const char* description,
                         OffHeapObjectSlot start,
                         OffHeapObjectSlot end) override {
    DCHECK_EQ(root, Root::kStringTable);
1604
    PtrComprCageBase cage_base = Isolate::FromHeap(explorer_->heap_);
1605 1606
    for (OffHeapObjectSlot p = start; p < end; ++p) {
      explorer_->SetGcSubrootReference(root, description, visiting_weak_roots_,
1607
                                       p.load(cage_base));
1608 1609 1610
    }
  }

1611
 private:
1612 1613
  V8HeapExplorer* explorer_;
  bool visiting_weak_roots_;
1614 1615
};

1616 1617 1618
bool V8HeapExplorer::IterateAndExtractReferences(
    HeapSnapshotGenerator* generator) {
  generator_ = generator;
1619

1620 1621
  // Create references to the synthetic roots.
  SetRootGcRootsReference();
1622 1623
  for (int root = 0; root < static_cast<int>(Root::kNumberOfRoots); root++) {
    SetGcRootsReference(static_cast<Root>(root));
1624 1625
  }

1626 1627 1628
  // Make sure builtin code objects get their builtin tags
  // first. Otherwise a particular JSFunction object could set
  // its custom name to a generic builtin.
1629
  RootsReferencesExtractor extractor(this);
Dan Elphick's avatar
Dan Elphick committed
1630
  ReadOnlyRoots(heap_).Iterate(&extractor);
1631
  heap_->IterateRoots(&extractor, base::EnumSet<SkipRoot>{SkipRoot::kWeak});
1632
  // TODO(v8:11800): The heap snapshot generator incorrectly considers the weak
1633 1634 1635
  // string tables as strong retainers. Move IterateWeakRoots after
  // SetVisitingWeakRoots.
  heap_->IterateWeakRoots(&extractor, {});
1636 1637
  extractor.SetVisitingWeakRoots();
  heap_->IterateWeakGlobalHandles(&extractor);
1638 1639

  bool interrupted = false;
1640

1641 1642
  CombinedHeapObjectIterator iterator(heap_,
                                      HeapObjectIterator::kFilterUnreachable);
1643
  // Heap iteration with filtering must be finished in any case.
1644 1645
  for (HeapObject obj = iterator.Next(); !obj.is_null();
       obj = iterator.Next(), progress_->ProgressStep()) {
1646 1647
    if (interrupted) continue;

1648
    size_t max_pointer = obj.Size() / kTaggedSize;
1649
    if (max_pointer > visited_fields_.size()) {
1650
      // Clear the current bits.
1651
      std::vector<bool>().swap(visited_fields_);
1652
      // Reallocate to right size.
1653
      visited_fields_.resize(max_pointer, false);
1654 1655
    }

1656
    HeapEntry* entry = GetEntry(obj);
1657
    ExtractReferences(entry, obj);
1658
    SetInternalReference(entry, "map", obj.map(), HeapObject::kMapOffset);
1659 1660 1661
    // Extract unvisited fields as hidden references and restore tags
    // of visited fields.
    IndexedReferencesExtractor refs_extractor(this, obj, entry);
1662
    obj.Iterate(&refs_extractor);
1663

1664 1665 1666 1667
    // Ensure visited_fields_ doesn't leak to the next object.
    for (size_t i = 0; i < max_pointer; ++i) {
      DCHECK(!visited_fields_[i]);
    }
1668

1669 1670 1671
    // Extract location for specific object types
    ExtractLocation(entry, obj);

1672 1673
    if (!progress_->ProgressReport(false)) interrupted = true;
  }
1674

1675
  generator_ = nullptr;
1676
  return interrupted ? false : progress_->ProgressReport(true);
1677 1678
}

1679
bool V8HeapExplorer::IsEssentialObject(Object object) {
1680
  ReadOnlyRoots roots(heap_);
1681
  return object.IsHeapObject() && !object.IsOddball() &&
1682 1683 1684 1685 1686 1687 1688 1689 1690 1691
         object != roots.empty_byte_array() &&
         object != roots.empty_fixed_array() &&
         object != roots.empty_weak_fixed_array() &&
         object != roots.empty_descriptor_array() &&
         object != roots.fixed_array_map() && object != roots.cell_map() &&
         object != roots.global_property_cell_map() &&
         object != roots.shared_function_info_map() &&
         object != roots.free_space_map() &&
         object != roots.one_pointer_filler_map() &&
         object != roots.two_pointer_filler_map();
1692 1693
}

1694
bool V8HeapExplorer::IsEssentialHiddenReference(Object parent,
1695
                                                int field_offset) {
1696
  if (parent.IsAllocationSite() &&
1697 1698
      field_offset == AllocationSite::kWeakNextOffset)
    return false;
1699
  if (parent.IsCodeDataContainer() &&
1700
      field_offset == CodeDataContainer::kNextCodeLinkOffset)
1701
    return false;
1702
  if (parent.IsContext() &&
1703 1704
      field_offset == Context::OffsetOfElementAt(Context::NEXT_CONTEXT_LINK))
    return false;
1705 1706
  return true;
}
1707

1708
void V8HeapExplorer::SetContextReference(HeapEntry* parent_entry,
1709
                                         String reference_name,
1710
                                         Object child_obj, int field_offset) {
1711
  HeapEntry* child_entry = GetEntry(child_obj);
1712
  if (child_entry == nullptr) return;
1713 1714
  parent_entry->SetNamedReference(HeapGraphEdge::kContextVariable,
                                  names_->GetName(reference_name), child_entry);
1715
  MarkVisitedField(field_offset);
1716 1717
}

1718
void V8HeapExplorer::MarkVisitedField(int offset) {
1719
  if (offset < 0) return;
1720
  int index = offset / kTaggedSize;
1721 1722
  DCHECK(!visited_fields_[index]);
  visited_fields_[index] = true;
1723 1724
}

1725
void V8HeapExplorer::SetNativeBindReference(HeapEntry* parent_entry,
1726
                                            const char* reference_name,
1727
                                            Object child_obj) {
1728
  HeapEntry* child_entry = GetEntry(child_obj);
1729
  if (child_entry == nullptr) return;
1730 1731
  parent_entry->SetNamedReference(HeapGraphEdge::kShortcut, reference_name,
                                  child_entry);
1732 1733
}

1734
void V8HeapExplorer::SetElementReference(HeapEntry* parent_entry, int index,
1735
                                         Object child_obj) {
1736
  HeapEntry* child_entry = GetEntry(child_obj);
1737
  if (child_entry == nullptr) return;
1738 1739
  parent_entry->SetIndexedReference(HeapGraphEdge::kElement, index,
                                    child_entry);
1740 1741
}

1742
void V8HeapExplorer::SetInternalReference(HeapEntry* parent_entry,
1743
                                          const char* reference_name,
1744
                                          Object child_obj, int field_offset) {
Arno Renevier's avatar
Arno Renevier committed
1745 1746
  if (!IsEssentialObject(child_obj)) {
    return;
1747
  }
Arno Renevier's avatar
Arno Renevier committed
1748 1749 1750 1751
  HeapEntry* child_entry = GetEntry(child_obj);
  DCHECK_NOT_NULL(child_entry);
  parent_entry->SetNamedReference(HeapGraphEdge::kInternal, reference_name,
                                  child_entry);
1752
  MarkVisitedField(field_offset);
1753 1754
}

1755
void V8HeapExplorer::SetInternalReference(HeapEntry* parent_entry, int index,
1756
                                          Object child_obj, int field_offset) {
Arno Renevier's avatar
Arno Renevier committed
1757 1758
  if (!IsEssentialObject(child_obj)) {
    return;
1759
  }
Arno Renevier's avatar
Arno Renevier committed
1760 1761 1762 1763
  HeapEntry* child_entry = GetEntry(child_obj);
  DCHECK_NOT_NULL(child_entry);
  parent_entry->SetNamedReference(HeapGraphEdge::kInternal,
                                  names_->GetName(index), child_entry);
1764
  MarkVisitedField(field_offset);
1765 1766
}

1767
void V8HeapExplorer::SetHiddenReference(HeapObject parent_obj,
1768
                                        HeapEntry* parent_entry, int index,
1769
                                        Object child_obj, int field_offset) {
1770
  DCHECK_EQ(parent_entry, GetEntry(parent_obj));
1771
  DCHECK(!MapWord::IsPacked(child_obj.ptr()));
Arno Renevier's avatar
Arno Renevier committed
1772 1773 1774
  if (!IsEssentialObject(child_obj)) {
    return;
  }
1775
  HeapEntry* child_entry = GetEntry(child_obj);
Arno Renevier's avatar
Arno Renevier committed
1776 1777
  DCHECK_NOT_NULL(child_entry);
  if (IsEssentialHiddenReference(parent_obj, field_offset)) {
1778 1779
    parent_entry->SetIndexedReference(HeapGraphEdge::kHidden, index,
                                      child_entry);
1780 1781 1782
  }
}

1783
void V8HeapExplorer::SetWeakReference(HeapEntry* parent_entry,
1784
                                      const char* reference_name,
1785
                                      Object child_obj, int field_offset) {
Arno Renevier's avatar
Arno Renevier committed
1786 1787
  if (!IsEssentialObject(child_obj)) {
    return;
1788
  }
Arno Renevier's avatar
Arno Renevier committed
1789 1790 1791 1792
  HeapEntry* child_entry = GetEntry(child_obj);
  DCHECK_NOT_NULL(child_entry);
  parent_entry->SetNamedReference(HeapGraphEdge::kWeak, reference_name,
                                  child_entry);
1793
  MarkVisitedField(field_offset);
1794 1795
}

1796
void V8HeapExplorer::SetWeakReference(HeapEntry* parent_entry, int index,
1797 1798
                                      Object child_obj,
                                      base::Optional<int> field_offset) {
Arno Renevier's avatar
Arno Renevier committed
1799 1800
  if (!IsEssentialObject(child_obj)) {
    return;
1801
  }
Arno Renevier's avatar
Arno Renevier committed
1802 1803 1804 1805
  HeapEntry* child_entry = GetEntry(child_obj);
  DCHECK_NOT_NULL(child_entry);
  parent_entry->SetNamedReference(
      HeapGraphEdge::kWeak, names_->GetFormatted("%d", index), child_entry);
1806 1807 1808
  if (field_offset.has_value()) {
    MarkVisitedField(*field_offset);
  }
1809 1810
}

1811
void V8HeapExplorer::SetDataOrAccessorPropertyReference(
1812
    PropertyKind kind, HeapEntry* parent_entry, Name reference_name,
1813
    Object child_obj, const char* name_format_string, int field_offset) {
1814
  if (kind == kAccessor) {
1815 1816
    ExtractAccessorPairProperty(parent_entry, reference_name, child_obj,
                                field_offset);
1817
  } else {
1818
    SetPropertyReference(parent_entry, reference_name, child_obj,
1819 1820 1821 1822
                         name_format_string, field_offset);
  }
}

1823
void V8HeapExplorer::SetPropertyReference(HeapEntry* parent_entry,
1824
                                          Name reference_name, Object child_obj,
1825 1826
                                          const char* name_format_string,
                                          int field_offset) {
1827
  HeapEntry* child_entry = GetEntry(child_obj);
1828 1829
  if (child_entry == nullptr) return;
  HeapGraphEdge::Type type =
1830
      reference_name.IsSymbol() || String::cast(reference_name).length() > 0
1831 1832 1833
          ? HeapGraphEdge::kProperty
          : HeapGraphEdge::kInternal;
  const char* name =
1834
      name_format_string != nullptr && reference_name.IsString()
1835 1836 1837
          ? names_->GetFormatted(
                name_format_string,
                String::cast(reference_name)
1838
                    .ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL)
1839 1840 1841
                    .get())
          : names_->GetName(reference_name);

1842
  parent_entry->SetNamedReference(type, name, child_entry);
1843
  MarkVisitedField(field_offset);
1844 1845 1846
}

void V8HeapExplorer::SetRootGcRootsReference() {
1847 1848
  snapshot_->root()->SetIndexedAutoIndexReference(HeapGraphEdge::kElement,
                                                  snapshot_->gc_roots());
1849 1850
}

1851
void V8HeapExplorer::SetUserGlobalReference(Object child_obj) {
1852
  HeapEntry* child_entry = GetEntry(child_obj);
1853
  DCHECK_NOT_NULL(child_entry);
1854 1855
  snapshot_->root()->SetNamedAutoIndexReference(HeapGraphEdge::kShortcut,
                                                nullptr, child_entry, names_);
1856 1857
}

1858
void V8HeapExplorer::SetGcRootsReference(Root root) {
1859 1860
  snapshot_->gc_roots()->SetIndexedAutoIndexReference(
      HeapGraphEdge::kElement, snapshot_->gc_subroot(root));
1861 1862
}

1863
void V8HeapExplorer::SetGcSubrootReference(Root root, const char* description,
1864
                                           bool is_weak, Object child_obj) {
Arno Renevier's avatar
Arno Renevier committed
1865 1866 1867 1868 1869 1870 1871
  if (child_obj.IsSmi()) {
    // TODO(arenevier): if we handle smis here, the snapshot gets 2 to 3 times
    // slower on large heaps. According to perf, The bulk of the extra works
    // happens in TemplateHashMapImpl::Probe method, when tyring to get
    // names->GetFormatted("%d / %s", index, description)
    return;
  }
1872
  HeapEntry* child_entry = GetEntry(child_obj);
1873 1874
  if (child_entry == nullptr) return;
  const char* name = GetStrongGcSubrootName(child_obj);
1875 1876
  HeapGraphEdge::Type edge_type =
      is_weak ? HeapGraphEdge::kWeak : HeapGraphEdge::kInternal;
1877
  if (name != nullptr) {
1878 1879
    snapshot_->gc_subroot(root)->SetNamedReference(edge_type, name,
                                                   child_entry);
1880
  } else {
1881 1882
    snapshot_->gc_subroot(root)->SetNamedAutoIndexReference(
        edge_type, description, child_entry, names_);
1883 1884
  }

1885 1886
  // For full heap snapshots we do not emit user roots but rather rely on
  // regular GC roots to retain objects.
1887
  if (!snapshot_->treat_global_objects_as_roots()) return;
1888

1889 1890 1891
  // Add a shortcut to JS global object reference at snapshot root.
  // That allows the user to easily find global objects. They are
  // also used as starting points in distance calculations.
1892
  if (is_weak || !child_obj.IsNativeContext()) return;
1893

1894 1895
  JSGlobalObject global = Context::cast(child_obj).global_object();
  if (!global.IsJSGlobalObject()) return;
1896

1897
  if (!user_roots_.insert(global).second) return;
1898 1899 1900

  SetUserGlobalReference(global);
}
1901

1902
const char* V8HeapExplorer::GetStrongGcSubrootName(Object object) {
1903
  if (strong_gc_subroot_names_.empty()) {
1904
    Isolate* isolate = Isolate::FromHeap(heap_);
Dan Elphick's avatar
Dan Elphick committed
1905 1906
    for (RootIndex root_index = RootIndex::kFirstStrongOrReadOnlyRoot;
         root_index <= RootIndex::kLastStrongOrReadOnlyRoot; ++root_index) {
1907 1908
      const char* name = RootsTable::name(root_index);
      strong_gc_subroot_names_.emplace(isolate->root(root_index), name);
1909
    }
1910
    CHECK(!strong_gc_subroot_names_.empty());
1911
  }
1912 1913
  auto it = strong_gc_subroot_names_.find(object);
  return it != strong_gc_subroot_names_.end() ? it->second : nullptr;
1914 1915
}

1916
void V8HeapExplorer::TagObject(Object obj, const char* tag) {
1917 1918 1919 1920 1921 1922 1923 1924
  if (IsEssentialObject(obj)) {
    HeapEntry* entry = GetEntry(obj);
    if (entry->name()[0] == '\0') {
      entry->set_name(tag);
    }
  }
}

1925
class GlobalObjectsEnumerator : public RootVisitor {
1926
 public:
1927 1928
  explicit GlobalObjectsEnumerator(Isolate* isolate) : isolate_(isolate) {}

1929 1930
  void VisitRootPointers(Root root, const char* description,
                         FullObjectSlot start, FullObjectSlot end) override {
1931
    VisitRootPointersImpl(root, description, start, end);
1932
  }
1933 1934 1935 1936 1937 1938 1939

  void VisitRootPointers(Root root, const char* description,
                         OffHeapObjectSlot start,
                         OffHeapObjectSlot end) override {
    VisitRootPointersImpl(root, description, start, end);
  }

1940
  int count() const { return static_cast<int>(objects_.size()); }
1941 1942 1943
  Handle<JSGlobalObject>& at(int i) { return objects_[i]; }

 private:
1944 1945 1946 1947
  template <typename TSlot>
  void VisitRootPointersImpl(Root root, const char* description, TSlot start,
                             TSlot end) {
    for (TSlot p = start; p < end; ++p) {
1948
      DCHECK(!MapWord::IsPacked(p.Relaxed_Load(isolate_).ptr()));
1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959
      Object o = p.load(isolate_);
      if (!o.IsNativeContext(isolate_)) continue;
      JSObject proxy = Context::cast(o).global_proxy();
      if (!proxy.IsJSGlobalProxy(isolate_)) continue;
      Object global = proxy.map(isolate_).prototype(isolate_);
      if (!global.IsJSGlobalObject(isolate_)) continue;
      objects_.push_back(handle(JSGlobalObject::cast(global), isolate_));
    }
  }

  Isolate* isolate_;
1960
  std::vector<Handle<JSGlobalObject>> objects_;
1961 1962 1963 1964
};


// Modifies heap. Must not be run during heap traversal.
1965 1966 1967
void V8HeapExplorer::CollectGlobalObjectsTags() {
  if (!global_object_name_resolver_) return;

1968
  Isolate* isolate = Isolate::FromHeap(heap_);
1969
  GlobalObjectsEnumerator enumerator(isolate);
1970 1971
  isolate->global_handles()->IterateAllRoots(&enumerator);
  for (int i = 0, l = enumerator.count(); i < l; ++i) {
1972 1973 1974 1975 1976 1977
    Handle<JSGlobalObject> obj = enumerator.at(i);
    const char* tag = global_object_name_resolver_->GetName(
        Utils::ToLocal(Handle<JSObject>::cast(obj)));
    if (tag) {
      global_object_tag_pairs_.emplace_back(obj, tag);
    }
1978
  }
1979
}
1980

1981 1982 1983 1984
void V8HeapExplorer::MakeGlobalObjectTagMap(
    const SafepointScope& safepoint_scope) {
  for (const auto& pair : global_object_tag_pairs_) {
    global_object_tag_map_.emplace(*pair.first, pair.second);
1985 1986 1987
  }
}

1988 1989 1990 1991 1992
class EmbedderGraphImpl : public EmbedderGraph {
 public:
  struct Edge {
    Node* from;
    Node* to;
1993
    const char* name;
1994 1995 1996 1997
  };

  class V8NodeImpl : public Node {
   public:
1998 1999
    explicit V8NodeImpl(Object object) : object_(object) {}
    Object GetObject() { return object_; }
2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012

    // Node overrides.
    bool IsEmbedderNode() override { return false; }
    const char* Name() override {
      // The name should be retrieved via GetObject().
      UNREACHABLE();
    }
    size_t SizeInBytes() override {
      // The size should be retrieved via GetObject().
      UNREACHABLE();
    }

   private:
2013
    Object object_;
2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027
  };

  Node* V8Node(const v8::Local<v8::Value>& value) final {
    Handle<Object> object = v8::Utils::OpenHandle(*value);
    DCHECK(!object.is_null());
    return AddNode(std::unique_ptr<Node>(new V8NodeImpl(*object)));
  }

  Node* AddNode(std::unique_ptr<Node> node) final {
    Node* result = node.get();
    nodes_.push_back(std::move(node));
    return result;
  }

2028 2029 2030
  void AddEdge(Node* from, Node* to, const char* name) final {
    edges_.push_back({from, to, name});
  }
2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041

  const std::vector<std::unique_ptr<Node>>& nodes() { return nodes_; }
  const std::vector<Edge>& edges() { return edges_; }

 private:
  std::vector<std::unique_ptr<Node>> nodes_;
  std::vector<Edge> edges_;
};

class EmbedderGraphEntriesAllocator : public HeapEntriesAllocator {
 public:
2042
  explicit EmbedderGraphEntriesAllocator(HeapSnapshot* snapshot)
2043 2044
      : snapshot_(snapshot),
        names_(snapshot_->profiler()->names()),
2045
        heap_object_map_(snapshot_->profiler()->heap_object_map()) {}
2046
  HeapEntry* AllocateEntry(HeapThing ptr) override;
Arno Renevier's avatar
Arno Renevier committed
2047
  HeapEntry* AllocateEntry(Smi smi) override;
2048 2049 2050 2051 2052 2053 2054

 private:
  HeapSnapshot* snapshot_;
  StringsStorage* names_;
  HeapObjectsMap* heap_object_map_;
};

2055 2056 2057 2058
namespace {

const char* EmbedderGraphNodeName(StringsStorage* names,
                                  EmbedderGraphImpl::Node* node) {
2059 2060 2061
  const char* prefix = node->NamePrefix();
  return prefix ? names->GetFormatted("%s %s", prefix, node->Name())
                : names->GetCopy(node->Name());
2062 2063 2064
}

HeapEntry::Type EmbedderGraphNodeType(EmbedderGraphImpl::Node* node) {
2065
  return node->IsRootNode() ? HeapEntry::kSynthetic : HeapEntry::kNative;
2066 2067 2068 2069 2070 2071 2072 2073
}

// Merges the names of an embedder node and its wrapper node.
// If the wrapper node name contains a tag suffix (part after '/') then the
// result is the embedder node name concatenated with the tag suffix.
// Otherwise, the result is the embedder node name.
const char* MergeNames(StringsStorage* names, const char* embedder_name,
                       const char* wrapper_name) {
2074 2075 2076
  const char* suffix = strchr(wrapper_name, '/');
  return suffix ? names->GetFormatted("%s %s", embedder_name, suffix)
                : embedder_name;
2077 2078 2079 2080
}

}  // anonymous namespace

2081 2082 2083 2084 2085
HeapEntry* EmbedderGraphEntriesAllocator::AllocateEntry(HeapThing ptr) {
  EmbedderGraphImpl::Node* node =
      reinterpret_cast<EmbedderGraphImpl::Node*>(ptr);
  DCHECK(node->IsEmbedderNode());
  size_t size = node->SizeInBytes();
2086 2087 2088 2089 2090
  Address lookup_address = reinterpret_cast<Address>(node->GetNativeObject());
  SnapshotObjectId id =
      (lookup_address) ? heap_object_map_->FindOrAddEntry(lookup_address, 0)
                       : static_cast<SnapshotObjectId>(
                             reinterpret_cast<uintptr_t>(node) << 1);
2091 2092 2093 2094 2095
  auto* heap_entry = snapshot_->AddEntry(EmbedderGraphNodeType(node),
                                         EmbedderGraphNodeName(names_, node),
                                         id, static_cast<int>(size), 0);
  heap_entry->set_detachedness(node->GetDetachedness());
  return heap_entry;
2096 2097
}

Arno Renevier's avatar
Arno Renevier committed
2098 2099 2100 2101 2102
HeapEntry* EmbedderGraphEntriesAllocator::AllocateEntry(Smi smi) {
  DCHECK(false);
  return nullptr;
}

2103
NativeObjectsExplorer::NativeObjectsExplorer(
2104
    HeapSnapshot* snapshot, SnapshottingProgressReportingInterface* progress)
2105 2106
    : isolate_(
          Isolate::FromHeap(snapshot->profiler()->heap_object_map()->heap())),
2107
      snapshot_(snapshot),
2108
      names_(snapshot_->profiler()->names()),
2109
      heap_object_map_(snapshot_->profiler()->heap_object_map()),
2110
      embedder_graph_entries_allocator_(
2111
          new EmbedderGraphEntriesAllocator(snapshot)) {}
2112

2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135
void NativeObjectsExplorer::MergeNodeIntoEntry(
    HeapEntry* entry, EmbedderGraph::Node* original_node,
    EmbedderGraph::Node* wrapper_node) {
  // The wrapper node may be an embedder node (for testing purposes) or a V8
  // node (production code).
  if (!wrapper_node->IsEmbedderNode()) {
    // For V8 nodes only we can add a lookup.
    EmbedderGraphImpl::V8NodeImpl* v8_node =
        static_cast<EmbedderGraphImpl::V8NodeImpl*>(wrapper_node);
    Object object = v8_node->GetObject();
    DCHECK(!object.IsSmi());
    if (original_node->GetNativeObject()) {
      HeapObject heap_object = HeapObject::cast(object);
      heap_object_map_->AddMergedNativeEntry(original_node->GetNativeObject(),
                                             heap_object.address());
      DCHECK_EQ(entry->id(), heap_object_map_->FindMergedNativeEntry(
                                 original_node->GetNativeObject()));
    }
  }
  entry->set_detachedness(original_node->GetDetachedness());
  entry->set_name(MergeNames(
      names_, EmbedderGraphNodeName(names_, original_node), entry->name()));
  entry->set_type(EmbedderGraphNodeType(original_node));
2136 2137 2138
  DCHECK_GE(entry->self_size() + original_node->SizeInBytes(),
            entry->self_size());
  entry->add_self_size(original_node->SizeInBytes());
2139 2140
}

2141 2142
HeapEntry* NativeObjectsExplorer::EntryForEmbedderGraphNode(
    EmbedderGraphImpl::Node* node) {
2143 2144 2145
  // Return the entry for the wrapper node if present.
  if (node->WrapperNode()) {
    node = node->WrapperNode();
2146
  }
2147
  // Node is EmbedderNode.
2148
  if (node->IsEmbedderNode()) {
2149 2150
    return generator_->FindOrAddEntry(node,
                                      embedder_graph_entries_allocator_.get());
2151
  }
2152 2153 2154 2155 2156 2157 2158
  // Node is V8NodeImpl.
  Object object =
      static_cast<EmbedderGraphImpl::V8NodeImpl*>(node)->GetObject();
  if (object.IsSmi()) return nullptr;
  auto* entry = generator_->FindEntry(
      reinterpret_cast<void*>(Object::cast(object).ptr()));
  return entry;
2159
}
2160 2161

bool NativeObjectsExplorer::IterateAndExtractReferences(
2162 2163
    HeapSnapshotGenerator* generator) {
  generator_ = generator;
2164

2165 2166
  if (FLAG_heap_profiler_use_embedder_graph &&
      snapshot_->profiler()->HasBuildEmbedderGraphCallback()) {
2167
    v8::HandleScope scope(reinterpret_cast<v8::Isolate*>(isolate_));
2168
    DisallowGarbageCollection no_gc;
2169 2170 2171
    EmbedderGraphImpl graph;
    snapshot_->profiler()->BuildEmbedderGraph(isolate_, &graph);
    for (const auto& node : graph.nodes()) {
2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183
      // Only add embedder nodes as V8 nodes have been added already by the
      // V8HeapExplorer.
      if (!node->IsEmbedderNode()) continue;

      if (auto* entry = EntryForEmbedderGraphNode(node.get())) {
        if (node->IsRootNode()) {
          snapshot_->root()->SetIndexedAutoIndexReference(
              HeapGraphEdge::kElement, entry);
        }
        if (node->WrapperNode()) {
          MergeNodeIntoEntry(entry, node.get(), node->WrapperNode());
        }
2184
      }
2185 2186 2187
    }
    // Fill edges of the graph.
    for (const auto& edge : graph.edges()) {
2188
      // |from| and |to| can be nullptr if the corresponding node is a V8 node
2189
      // pointing to a Smi.
2190
      HeapEntry* from = EntryForEmbedderGraphNode(edge.from);
2191 2192
      if (!from) continue;
      HeapEntry* to = EntryForEmbedderGraphNode(edge.to);
2193 2194 2195 2196
      if (!to) continue;
      if (edge.name == nullptr) {
        from->SetIndexedAutoIndexReference(HeapGraphEdge::kElement, to);
      } else {
2197 2198
        from->SetNamedReference(HeapGraphEdge::kInternal,
                                names_->GetCopy(edge.name), to);
2199 2200
      }
    }
2201
  }
2202
  generator_ = nullptr;
2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217
  return true;
}

HeapSnapshotGenerator::HeapSnapshotGenerator(
    HeapSnapshot* snapshot,
    v8::ActivityControl* control,
    v8::HeapProfiler::ObjectNameResolver* resolver,
    Heap* heap)
    : snapshot_(snapshot),
      control_(control),
      v8_heap_explorer_(snapshot_, this, resolver),
      dom_explorer_(snapshot_, this),
      heap_(heap) {
}

2218
namespace {
2219
class V8_NODISCARD NullContextForSnapshotScope {
2220
 public:
2221
  explicit NullContextForSnapshotScope(Isolate* isolate)
2222
      : isolate_(isolate), prev_(isolate->context()) {
2223
    isolate_->set_context(Context());
2224
  }
2225
  ~NullContextForSnapshotScope() { isolate_->set_context(prev_); }
2226 2227 2228

 private:
  Isolate* isolate_;
2229
  Context prev_;
2230
};
2231
}  // namespace
2232 2233

bool HeapSnapshotGenerator::GenerateSnapshot() {
2234 2235 2236
  Isolate* isolate = Isolate::FromHeap(heap_);
  base::Optional<HandleScope> handle_scope(base::in_place, isolate);
  v8_heap_explorer_.CollectGlobalObjectsTags();
2237

2238
  heap_->CollectAllAvailableGarbage(GarbageCollectionReason::kHeapProfiler);
2239

2240
  NullContextForSnapshotScope null_context_scope(isolate);
2241
  SafepointScope scope(heap_);
2242 2243
  v8_heap_explorer_.MakeGlobalObjectTagMap(scope);
  handle_scope.reset();
2244

2245
#ifdef VERIFY_HEAP
2246
  Heap* debug_heap = heap_;
2247 2248 2249
  if (FLAG_verify_heap) {
    debug_heap->Verify();
  }
2250 2251
#endif

2252
  InitProgressCounter();
2253 2254

#ifdef VERIFY_HEAP
2255 2256 2257
  if (FLAG_verify_heap) {
    debug_heap->Verify();
  }
2258 2259
#endif

2260 2261
  snapshot_->AddSyntheticRootEntries();

2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277
  if (!FillReferences()) return false;

  snapshot_->FillChildren();
  snapshot_->RememberLastJSObjectId();

  progress_counter_ = progress_total_;
  if (!ProgressReport(true)) return false;
  return true;
}

void HeapSnapshotGenerator::ProgressStep() {
  ++progress_counter_;
}

bool HeapSnapshotGenerator::ProgressReport(bool force) {
  const int kProgressReportGranularity = 10000;
2278 2279 2280 2281
  if (control_ != nullptr &&
      (force || progress_counter_ % kProgressReportGranularity == 0)) {
    return control_->ReportProgressValue(progress_counter_, progress_total_) ==
           v8::ActivityControl::kContinue;
2282 2283 2284 2285
  }
  return true;
}

2286
void HeapSnapshotGenerator::InitProgressCounter() {
2287
  if (control_ == nullptr) return;
2288 2289 2290 2291 2292
  // The +1 ensures that intermediate ProgressReport calls will never signal
  // that the work is finished (i.e. progress_counter_ == progress_total_).
  // Only the forced ProgressReport() at the end of GenerateSnapshot()
  // should signal that the work is finished because signalling finished twice
  // breaks the DevTools frontend.
2293
  progress_total_ = v8_heap_explorer_.EstimateObjectsCount() + 1;
2294 2295 2296 2297
  progress_counter_ = 0;
}

bool HeapSnapshotGenerator::FillReferences() {
2298 2299
  return v8_heap_explorer_.IterateAndExtractReferences(this) &&
         dom_explorer_.IterateAndExtractReferences(this);
2300 2301 2302
}

template<int bytes> struct MaxDecimalDigitsIn;
2303 2304 2305 2306 2307
template <>
struct MaxDecimalDigitsIn<1> {
  static const int kSigned = 3;
  static const int kUnsigned = 3;
};
2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324
template<> struct MaxDecimalDigitsIn<4> {
  static const int kSigned = 11;
  static const int kUnsigned = 10;
};
template<> struct MaxDecimalDigitsIn<8> {
  static const int kSigned = 20;
  static const int kUnsigned = 20;
};

class OutputStreamWriter {
 public:
  explicit OutputStreamWriter(v8::OutputStream* stream)
      : stream_(stream),
        chunk_size_(stream->GetChunkSize()),
        chunk_(chunk_size_),
        chunk_pos_(0),
        aborted_(false) {
2325
    DCHECK_GT(chunk_size_, 0);
2326 2327 2328
  }
  bool aborted() { return aborted_; }
  void AddCharacter(char c) {
2329
    DCHECK_NE(c, '\0');
2330
    DCHECK(chunk_pos_ < chunk_size_);
2331 2332 2333 2334
    chunk_[chunk_pos_++] = c;
    MaybeWriteChunk();
  }
  void AddString(const char* s) {
2335 2336 2337
    size_t len = strlen(s);
    DCHECK_GE(kMaxInt, len);
    AddSubstring(s, static_cast<int>(len));
2338 2339 2340
  }
  void AddSubstring(const char* s, int n) {
    if (n <= 0) return;
2341
    DCHECK_LE(n, strlen(s));
2342 2343
    const char* s_end = s + n;
    while (s < s_end) {
2344
      int s_chunk_size =
2345
          std::min(chunk_size_ - chunk_pos_, static_cast<int>(s_end - s));
2346
      DCHECK_GT(s_chunk_size, 0);
2347
      MemCopy(chunk_.begin() + chunk_pos_, s, s_chunk_size);
2348 2349 2350 2351 2352 2353 2354 2355
      s += s_chunk_size;
      chunk_pos_ += s_chunk_size;
      MaybeWriteChunk();
    }
  }
  void AddNumber(unsigned n) { AddNumberImpl<unsigned>(n, "%u"); }
  void Finalize() {
    if (aborted_) return;
2356
    DCHECK(chunk_pos_ < chunk_size_);
2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369
    if (chunk_pos_ != 0) {
      WriteChunk();
    }
    stream_->EndOfStream();
  }

 private:
  template<typename T>
  void AddNumberImpl(T n, const char* format) {
    // Buffer for the longest value plus trailing \0
    static const int kMaxNumberSize =
        MaxDecimalDigitsIn<sizeof(T)>::kUnsigned + 1;
    if (chunk_size_ - chunk_pos_ >= kMaxNumberSize) {
2370
      int result = SNPrintF(
2371
          chunk_.SubVector(chunk_pos_, chunk_size_), format, n);
2372
      DCHECK_NE(result, -1);
2373 2374 2375
      chunk_pos_ += result;
      MaybeWriteChunk();
    } else {
2376
      base::EmbeddedVector<char, kMaxNumberSize> buffer;
2377
      int result = SNPrintF(buffer, format, n);
2378
      USE(result);
2379
      DCHECK_NE(result, -1);
2380
      AddString(buffer.begin());
2381 2382 2383
    }
  }
  void MaybeWriteChunk() {
2384
    DCHECK(chunk_pos_ <= chunk_size_);
2385 2386 2387 2388 2389 2390
    if (chunk_pos_ == chunk_size_) {
      WriteChunk();
    }
  }
  void WriteChunk() {
    if (aborted_) return;
2391 2392 2393
    if (stream_->WriteAsciiChunk(chunk_.begin(), chunk_pos_) ==
        v8::OutputStream::kAbort)
      aborted_ = true;
2394 2395 2396 2397 2398
    chunk_pos_ = 0;
  }

  v8::OutputStream* stream_;
  int chunk_size_;
2399
  base::ScopedVector<char> chunk_;
2400 2401 2402 2403 2404 2405 2406
  int chunk_pos_;
  bool aborted_;
};


// type, name|index, to_node.
const int HeapSnapshotJSONSerializer::kEdgeFieldsCount = 3;
2407 2408
// type, name, id, self_size, edge_count, trace_node_id, detachedness.
const int HeapSnapshotJSONSerializer::kNodeFieldsCount = 7;
2409 2410

void HeapSnapshotJSONSerializer::Serialize(v8::OutputStream* stream) {
2411
  if (AllocationTracker* allocation_tracker =
2412
      snapshot_->profiler()->allocation_tracker()) {
2413 2414
    allocation_tracker->PrepareForSerialization();
  }
2415
  DCHECK_NULL(writer_);
2416 2417 2418
  writer_ = new OutputStreamWriter(stream);
  SerializeImpl();
  delete writer_;
2419
  writer_ = nullptr;
2420 2421 2422 2423
}


void HeapSnapshotJSONSerializer::SerializeImpl() {
2424
  DCHECK_EQ(0, snapshot_->root()->index());
2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437
  writer_->AddCharacter('{');
  writer_->AddString("\"snapshot\":{");
  SerializeSnapshot();
  if (writer_->aborted()) return;
  writer_->AddString("},\n");
  writer_->AddString("\"nodes\":[");
  SerializeNodes();
  if (writer_->aborted()) return;
  writer_->AddString("],\n");
  writer_->AddString("\"edges\":[");
  SerializeEdges();
  if (writer_->aborted()) return;
  writer_->AddString("],\n");
2438 2439 2440 2441 2442 2443 2444 2445 2446 2447

  writer_->AddString("\"trace_function_infos\":[");
  SerializeTraceNodeInfos();
  if (writer_->aborted()) return;
  writer_->AddString("],\n");
  writer_->AddString("\"trace_tree\":[");
  SerializeTraceTree();
  if (writer_->aborted()) return;
  writer_->AddString("],\n");

2448 2449 2450 2451 2452
  writer_->AddString("\"samples\":[");
  SerializeSamples();
  if (writer_->aborted()) return;
  writer_->AddString("],\n");

2453 2454 2455 2456 2457
  writer_->AddString("\"locations\":[");
  SerializeLocations();
  if (writer_->aborted()) return;
  writer_->AddString("],\n");

2458 2459 2460 2461 2462 2463 2464 2465 2466 2467
  writer_->AddString("\"strings\":[");
  SerializeStrings();
  if (writer_->aborted()) return;
  writer_->AddCharacter(']');
  writer_->AddCharacter('}');
  writer_->Finalize();
}


int HeapSnapshotJSONSerializer::GetStringId(const char* s) {
lpy's avatar
lpy committed
2468
  base::HashMap::Entry* cache_entry =
2469
      strings_.LookupOrInsert(const_cast<char*>(s), StringHash(s));
2470
  if (cache_entry->value == nullptr) {
2471 2472 2473 2474 2475 2476
    cache_entry->value = reinterpret_cast<void*>(next_string_id_++);
  }
  return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
}


2477 2478 2479 2480
namespace {

template<size_t size> struct ToUnsigned;

2481 2482 2483 2484 2485
template <>
struct ToUnsigned<1> {
  using Type = uint8_t;
};

2486
template<> struct ToUnsigned<4> {
2487
  using Type = uint32_t;
2488 2489 2490
};

template<> struct ToUnsigned<8> {
2491
  using Type = uint64_t;
2492 2493 2494 2495
};

}  // namespace

2496 2497 2498
template <typename T>
static int utoa_impl(T value, const base::Vector<char>& buffer,
                     int buffer_pos) {
2499
  STATIC_ASSERT(static_cast<T>(-1) > 0);  // Check that T is unsigned
2500
  int number_of_digits = 0;
2501
  T t = value;
2502 2503 2504 2505 2506 2507 2508
  do {
    ++number_of_digits;
  } while (t /= 10);

  buffer_pos += number_of_digits;
  int result = buffer_pos;
  do {
2509
    int last_digit = static_cast<int>(value % 10);
2510 2511 2512 2513 2514 2515
    buffer[--buffer_pos] = '0' + last_digit;
    value /= 10;
  } while (value);
  return result;
}

2516 2517
template <typename T>
static int utoa(T value, const base::Vector<char>& buffer, int buffer_pos) {
2518
  typename ToUnsigned<sizeof(value)>::Type unsigned_value = value;
2519
  STATIC_ASSERT(sizeof(value) == sizeof(unsigned_value));
2520 2521 2522
  return utoa_impl(unsigned_value, buffer, buffer_pos);
}

2523 2524 2525 2526
void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge,
                                               bool first_edge) {
  // The buffer needs space for 3 unsigned ints, 3 commas, \n and \0
  static const int kBufferSize =
2527
      MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned * 3 + 3 + 2;
2528
  base::EmbeddedVector<char, kBufferSize> buffer;
2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539
  int edge_name_or_index = edge->type() == HeapGraphEdge::kElement
      || edge->type() == HeapGraphEdge::kHidden
      ? edge->index() : GetStringId(edge->name());
  int buffer_pos = 0;
  if (!first_edge) {
    buffer[buffer_pos++] = ',';
  }
  buffer_pos = utoa(edge->type(), buffer, buffer_pos);
  buffer[buffer_pos++] = ',';
  buffer_pos = utoa(edge_name_or_index, buffer, buffer_pos);
  buffer[buffer_pos++] = ',';
2540
  buffer_pos = utoa(to_node_index(edge->to()), buffer, buffer_pos);
2541 2542
  buffer[buffer_pos++] = '\n';
  buffer[buffer_pos++] = '\0';
2543
  writer_->AddString(buffer.begin());
2544 2545 2546
}

void HeapSnapshotJSONSerializer::SerializeEdges() {
2547
  std::vector<HeapGraphEdge*>& edges = snapshot_->children();
2548
  for (size_t i = 0; i < edges.size(); ++i) {
2549
    DCHECK(i == 0 ||
2550 2551 2552 2553 2554 2555
           edges[i - 1]->from()->index() <= edges[i]->from()->index());
    SerializeEdge(edges[i], i == 0);
    if (writer_->aborted()) return;
  }
}

2556
void HeapSnapshotJSONSerializer::SerializeNode(const HeapEntry* entry) {
2557 2558
  // The buffer needs space for 5 unsigned ints, 1 size_t, 1 uint8_t, 7 commas,
  // \n and \0
2559
  static const int kBufferSize =
2560 2561 2562
      5 * MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned +
      MaxDecimalDigitsIn<sizeof(size_t)>::kUnsigned +
      MaxDecimalDigitsIn<sizeof(uint8_t)>::kUnsigned + 7 + 1 + 1;
2563
  base::EmbeddedVector<char, kBufferSize> buffer;
2564
  int buffer_pos = 0;
2565
  if (to_node_index(entry) != 0) {
2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576
    buffer[buffer_pos++] = ',';
  }
  buffer_pos = utoa(entry->type(), buffer, buffer_pos);
  buffer[buffer_pos++] = ',';
  buffer_pos = utoa(GetStringId(entry->name()), buffer, buffer_pos);
  buffer[buffer_pos++] = ',';
  buffer_pos = utoa(entry->id(), buffer, buffer_pos);
  buffer[buffer_pos++] = ',';
  buffer_pos = utoa(entry->self_size(), buffer, buffer_pos);
  buffer[buffer_pos++] = ',';
  buffer_pos = utoa(entry->children_count(), buffer, buffer_pos);
2577 2578
  buffer[buffer_pos++] = ',';
  buffer_pos = utoa(entry->trace_node_id(), buffer, buffer_pos);
2579 2580
  buffer[buffer_pos++] = ',';
  buffer_pos = utoa(entry->detachedness(), buffer, buffer_pos);
2581 2582
  buffer[buffer_pos++] = '\n';
  buffer[buffer_pos++] = '\0';
2583
  writer_->AddString(buffer.begin());
2584 2585 2586
}

void HeapSnapshotJSONSerializer::SerializeNodes() {
2587
  const std::deque<HeapEntry>& entries = snapshot_->entries();
2588 2589
  for (const HeapEntry& entry : entries) {
    SerializeNode(&entry);
2590 2591 2592 2593 2594
    if (writer_->aborted()) return;
  }
}

void HeapSnapshotJSONSerializer::SerializeSnapshot() {
2595
  writer_->AddString("\"meta\":");
2596 2597
  // The object describing node serialization layout.
  // We use a set of macros to improve readability.
2598 2599

// clang-format off
2600 2601 2602 2603 2604 2605 2606 2607 2608
#define JSON_A(s) "[" s "]"
#define JSON_O(s) "{" s "}"
#define JSON_S(s) "\"" s "\""
  writer_->AddString(JSON_O(
    JSON_S("node_fields") ":" JSON_A(
        JSON_S("type") ","
        JSON_S("name") ","
        JSON_S("id") ","
        JSON_S("self_size") ","
2609
        JSON_S("edge_count") ","
2610 2611
        JSON_S("trace_node_id") ","
        JSON_S("detachedness")) ","
2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622
    JSON_S("node_types") ":" JSON_A(
        JSON_A(
            JSON_S("hidden") ","
            JSON_S("array") ","
            JSON_S("string") ","
            JSON_S("object") ","
            JSON_S("code") ","
            JSON_S("closure") ","
            JSON_S("regexp") ","
            JSON_S("number") ","
            JSON_S("native") ","
2623 2624
            JSON_S("synthetic") ","
            JSON_S("concatenated string") ","
2625
            JSON_S("sliced string") ","
2626 2627
            JSON_S("symbol") ","
            JSON_S("bigint")) ","
2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647
        JSON_S("string") ","
        JSON_S("number") ","
        JSON_S("number") ","
        JSON_S("number") ","
        JSON_S("number") ","
        JSON_S("number")) ","
    JSON_S("edge_fields") ":" JSON_A(
        JSON_S("type") ","
        JSON_S("name_or_index") ","
        JSON_S("to_node")) ","
    JSON_S("edge_types") ":" JSON_A(
        JSON_A(
            JSON_S("context") ","
            JSON_S("element") ","
            JSON_S("property") ","
            JSON_S("internal") ","
            JSON_S("hidden") ","
            JSON_S("shortcut") ","
            JSON_S("weak")) ","
        JSON_S("string_or_number") ","
2648 2649 2650 2651 2652 2653 2654 2655 2656 2657
        JSON_S("node")) ","
    JSON_S("trace_function_info_fields") ":" JSON_A(
        JSON_S("function_id") ","
        JSON_S("name") ","
        JSON_S("script_name") ","
        JSON_S("script_id") ","
        JSON_S("line") ","
        JSON_S("column")) ","
    JSON_S("trace_node_fields") ":" JSON_A(
        JSON_S("id") ","
2658
        JSON_S("function_info_index") ","
2659 2660
        JSON_S("count") ","
        JSON_S("size") ","
2661 2662 2663
        JSON_S("children")) ","
    JSON_S("sample_fields") ":" JSON_A(
        JSON_S("timestamp_us") ","
2664 2665 2666 2667 2668 2669 2670
        JSON_S("last_assigned_id")) ","
    JSON_S("location_fields") ":" JSON_A(
        JSON_S("object_index") ","
        JSON_S("script_id") ","
        JSON_S("line") ","
        JSON_S("column"))));
// clang-format on
2671 2672 2673 2674
#undef JSON_S
#undef JSON_O
#undef JSON_A
  writer_->AddString(",\"node_count\":");
2675
  writer_->AddNumber(static_cast<unsigned>(snapshot_->entries().size()));
2676
  writer_->AddString(",\"edge_count\":");
2677
  writer_->AddNumber(static_cast<double>(snapshot_->edges().size()));
2678 2679
  writer_->AddString(",\"trace_function_count\":");
  uint32_t count = 0;
2680
  AllocationTracker* tracker = snapshot_->profiler()->allocation_tracker();
2681
  if (tracker) {
2682
    count = static_cast<uint32_t>(tracker->function_info_list().size());
2683 2684
  }
  writer_->AddNumber(count);
2685 2686 2687 2688 2689 2690
}


static void WriteUChar(OutputStreamWriter* w, unibrow::uchar u) {
  static const char hex_chars[] = "0123456789ABCDEF";
  w->AddString("\\u");
2691 2692 2693 2694
  w->AddCharacter(hex_chars[(u >> 12) & 0xF]);
  w->AddCharacter(hex_chars[(u >> 8) & 0xF]);
  w->AddCharacter(hex_chars[(u >> 4) & 0xF]);
  w->AddCharacter(hex_chars[u & 0xF]);
2695 2696
}

2697

2698
void HeapSnapshotJSONSerializer::SerializeTraceTree() {
2699
  AllocationTracker* tracker = snapshot_->profiler()->allocation_tracker();
2700 2701 2702 2703 2704 2705 2706 2707 2708
  if (!tracker) return;
  AllocationTraceTree* traces = tracker->trace_tree();
  SerializeTraceNode(traces->root());
}


void HeapSnapshotJSONSerializer::SerializeTraceNode(AllocationTraceNode* node) {
  // The buffer needs space for 4 unsigned ints, 4 commas, [ and \0
  const int kBufferSize =
2709
      4 * MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned + 4 + 1 + 1;
2710
  base::EmbeddedVector<char, kBufferSize> buffer;
2711 2712 2713
  int buffer_pos = 0;
  buffer_pos = utoa(node->id(), buffer, buffer_pos);
  buffer[buffer_pos++] = ',';
2714
  buffer_pos = utoa(node->function_info_index(), buffer, buffer_pos);
2715 2716 2717 2718 2719 2720 2721
  buffer[buffer_pos++] = ',';
  buffer_pos = utoa(node->allocation_count(), buffer, buffer_pos);
  buffer[buffer_pos++] = ',';
  buffer_pos = utoa(node->allocation_size(), buffer, buffer_pos);
  buffer[buffer_pos++] = ',';
  buffer[buffer_pos++] = '[';
  buffer[buffer_pos++] = '\0';
2722
  writer_->AddString(buffer.begin());
2723

2724 2725 2726
  int i = 0;
  for (AllocationTraceNode* child : node->children()) {
    if (i++ > 0) {
2727 2728
      writer_->AddCharacter(',');
    }
2729
    SerializeTraceNode(child);
2730 2731 2732 2733 2734 2735
  }
  writer_->AddCharacter(']');
}


// 0-based position is converted to 1-based during the serialization.
2736
static int SerializePosition(int position, const base::Vector<char>& buffer,
2737 2738 2739 2740
                             int buffer_pos) {
  if (position == -1) {
    buffer[buffer_pos++] = '0';
  } else {
2741
    DCHECK_GE(position, 0);
2742 2743 2744 2745 2746 2747
    buffer_pos = utoa(static_cast<unsigned>(position + 1), buffer, buffer_pos);
  }
  return buffer_pos;
}

void HeapSnapshotJSONSerializer::SerializeTraceNodeInfos() {
2748
  AllocationTracker* tracker = snapshot_->profiler()->allocation_tracker();
2749 2750 2751
  if (!tracker) return;
  // The buffer needs space for 6 unsigned ints, 6 commas, \n and \0
  const int kBufferSize =
2752
      6 * MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned + 6 + 1 + 1;
2753
  base::EmbeddedVector<char, kBufferSize> buffer;
2754 2755
  int i = 0;
  for (AllocationTracker::FunctionInfo* info : tracker->function_info_list()) {
2756
    int buffer_pos = 0;
2757
    if (i++ > 0) {
2758 2759
      buffer[buffer_pos++] = ',';
    }
2760
    buffer_pos = utoa(info->function_id, buffer, buffer_pos);
2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774
    buffer[buffer_pos++] = ',';
    buffer_pos = utoa(GetStringId(info->name), buffer, buffer_pos);
    buffer[buffer_pos++] = ',';
    buffer_pos = utoa(GetStringId(info->script_name), buffer, buffer_pos);
    buffer[buffer_pos++] = ',';
    // The cast is safe because script id is a non-negative Smi.
    buffer_pos = utoa(static_cast<unsigned>(info->script_id), buffer,
        buffer_pos);
    buffer[buffer_pos++] = ',';
    buffer_pos = SerializePosition(info->line, buffer, buffer_pos);
    buffer[buffer_pos++] = ',';
    buffer_pos = SerializePosition(info->column, buffer, buffer_pos);
    buffer[buffer_pos++] = '\n';
    buffer[buffer_pos++] = '\0';
2775
    writer_->AddString(buffer.begin());
2776 2777 2778 2779
  }
}


2780
void HeapSnapshotJSONSerializer::SerializeSamples() {
2781
  const std::vector<HeapObjectsMap::TimeInterval>& samples =
2782
      snapshot_->profiler()->heap_object_map()->samples();
2783
  if (samples.empty()) return;
2784 2785 2786 2787 2788 2789
  base::TimeTicks start_time = samples[0].timestamp;
  // The buffer needs space for 2 unsigned ints, 2 commas, \n and \0
  const int kBufferSize = MaxDecimalDigitsIn<sizeof(
                              base::TimeDelta().InMicroseconds())>::kUnsigned +
                          MaxDecimalDigitsIn<sizeof(samples[0].id)>::kUnsigned +
                          2 + 1 + 1;
2790
  base::EmbeddedVector<char, kBufferSize> buffer;
2791 2792
  int i = 0;
  for (const HeapObjectsMap::TimeInterval& sample : samples) {
2793
    int buffer_pos = 0;
2794
    if (i++ > 0) {
2795 2796 2797 2798 2799 2800 2801 2802
      buffer[buffer_pos++] = ',';
    }
    base::TimeDelta time_delta = sample.timestamp - start_time;
    buffer_pos = utoa(time_delta.InMicroseconds(), buffer, buffer_pos);
    buffer[buffer_pos++] = ',';
    buffer_pos = utoa(sample.last_assigned_id(), buffer, buffer_pos);
    buffer[buffer_pos++] = '\n';
    buffer[buffer_pos++] = '\0';
2803
    writer_->AddString(buffer.begin());
2804 2805 2806 2807
  }
}


2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840
void HeapSnapshotJSONSerializer::SerializeString(const unsigned char* s) {
  writer_->AddCharacter('\n');
  writer_->AddCharacter('\"');
  for ( ; *s != '\0'; ++s) {
    switch (*s) {
      case '\b':
        writer_->AddString("\\b");
        continue;
      case '\f':
        writer_->AddString("\\f");
        continue;
      case '\n':
        writer_->AddString("\\n");
        continue;
      case '\r':
        writer_->AddString("\\r");
        continue;
      case '\t':
        writer_->AddString("\\t");
        continue;
      case '\"':
      case '\\':
        writer_->AddCharacter('\\');
        writer_->AddCharacter(*s);
        continue;
      default:
        if (*s > 31 && *s < 128) {
          writer_->AddCharacter(*s);
        } else if (*s <= 31) {
          // Special character with no dedicated literal.
          WriteUChar(writer_, *s);
        } else {
          // Convert UTF-8 into \u UTF-16 literal.
2841
          size_t length = 1, cursor = 0;
2842 2843 2844 2845
          for ( ; length <= 4 && *(s + length) != '\0'; ++length) { }
          unibrow::uchar c = unibrow::Utf8::CalculateValue(s, length, &cursor);
          if (c != unibrow::Utf8::kBadChar) {
            WriteUChar(writer_, c);
2846
            DCHECK_NE(cursor, 0);
2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858
            s += cursor - 1;
          } else {
            writer_->AddCharacter('?');
          }
        }
    }
  }
  writer_->AddCharacter('\"');
}


void HeapSnapshotJSONSerializer::SerializeStrings() {
2859 2860
  base::ScopedVector<const unsigned char*> sorted_strings(strings_.occupancy() +
                                                          1);
2861
  for (base::HashMap::Entry* entry = strings_.Start(); entry != nullptr;
2862
       entry = strings_.Next(entry)) {
2863 2864
    int index = static_cast<int>(reinterpret_cast<uintptr_t>(entry->value));
    sorted_strings[index] = reinterpret_cast<const unsigned char*>(entry->key);
2865
  }
2866
  writer_->AddString("\"<dummy>\"");
2867
  for (int i = 1; i < sorted_strings.length(); ++i) {
2868
    writer_->AddCharacter(',');
2869
    SerializeString(sorted_strings[i]);
2870 2871 2872 2873
    if (writer_->aborted()) return;
  }
}

2874 2875 2876 2877 2878
void HeapSnapshotJSONSerializer::SerializeLocation(
    const SourceLocation& location) {
  // The buffer needs space for 4 unsigned ints, 3 commas, \n and \0
  static const int kBufferSize =
      MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned * 4 + 3 + 2;
2879
  base::EmbeddedVector<char, kBufferSize> buffer;
2880 2881 2882 2883 2884 2885 2886 2887 2888 2889
  int buffer_pos = 0;
  buffer_pos = utoa(to_node_index(location.entry_index), buffer, buffer_pos);
  buffer[buffer_pos++] = ',';
  buffer_pos = utoa(location.scriptId, buffer, buffer_pos);
  buffer[buffer_pos++] = ',';
  buffer_pos = utoa(location.line, buffer, buffer_pos);
  buffer[buffer_pos++] = ',';
  buffer_pos = utoa(location.col, buffer, buffer_pos);
  buffer[buffer_pos++] = '\n';
  buffer[buffer_pos++] = '\0';
2890
  writer_->AddString(buffer.begin());
2891 2892 2893 2894 2895 2896 2897 2898 2899 2900
}

void HeapSnapshotJSONSerializer::SerializeLocations() {
  const std::vector<SourceLocation>& locations = snapshot_->locations();
  for (size_t i = 0; i < locations.size(); i++) {
    if (i > 0) writer_->AddCharacter(',');
    SerializeLocation(locations[i]);
    if (writer_->aborted()) return;
  }
}
2901

2902 2903
}  // namespace internal
}  // namespace v8