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