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