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/vector.h"
37 : #include "src/visitors.h"
38 :
39 : namespace v8 {
40 : namespace internal {
41 :
42 0 : HeapGraphEdge::HeapGraphEdge(Type type, const char* name, HeapEntry* from,
43 : HeapEntry* to)
44 9843767 : : bit_field_(TypeField::encode(type) |
45 : FromIndexField::encode(from->index())),
46 : to_entry_(to),
47 9843767 : name_(name) {
48 : DCHECK(type == kContextVariable
49 : || type == kProperty
50 : || type == kInternal
51 : || type == kShortcut
52 : || type == kWeak);
53 0 : }
54 :
55 0 : HeapGraphEdge::HeapGraphEdge(Type type, int index, HeapEntry* from,
56 : HeapEntry* to)
57 1140476 : : bit_field_(TypeField::encode(type) |
58 : FromIndexField::encode(from->index())),
59 : to_entry_(to),
60 1140476 : index_(index) {
61 : DCHECK(type == kElement || type == kHidden);
62 0 : }
63 :
64 0 : HeapEntry::HeapEntry(HeapSnapshot* snapshot, int index, Type type,
65 : const char* name, SnapshotObjectId id, size_t self_size,
66 : unsigned trace_node_id)
67 : : type_(type),
68 : index_(index),
69 : children_count_(0),
70 : self_size_(self_size),
71 : snapshot_(snapshot),
72 : name_(name),
73 : id_(id),
74 3113073 : trace_node_id_(trace_node_id) {
75 : DCHECK_GE(index, 0);
76 0 : }
77 :
78 0 : void HeapEntry::SetNamedReference(HeapGraphEdge::Type type,
79 : const char* name,
80 : HeapEntry* entry) {
81 9843767 : ++children_count_;
82 19687534 : snapshot_->edges().emplace_back(type, name, this, entry);
83 0 : }
84 :
85 0 : void HeapEntry::SetIndexedReference(HeapGraphEdge::Type type,
86 : int index,
87 : HeapEntry* entry) {
88 1140476 : ++children_count_;
89 2280952 : snapshot_->edges().emplace_back(type, index, this, entry);
90 0 : }
91 :
92 1211383 : void HeapEntry::SetNamedAutoIndexReference(HeapGraphEdge::Type type,
93 : const char* description,
94 : HeapEntry* child,
95 : StringsStorage* names) {
96 1211383 : int index = children_count_ + 1;
97 : const char* name = description
98 : ? names->GetFormatted("%d / %s", index, description)
99 1211383 : : names->GetName(index);
100 : SetNamedReference(type, name, child);
101 1211383 : }
102 :
103 0 : void HeapEntry::Print(
104 : const char* prefix, const char* edge_name, int max_depth, int indent) {
105 : STATIC_ASSERT(sizeof(unsigned) == sizeof(id()));
106 : base::OS::Print("%6" PRIuS " @%6u %*c %s%s: ", self_size(), id(), indent, ' ',
107 0 : prefix, edge_name);
108 0 : if (type() != kString) {
109 0 : base::OS::Print("%s %.40s\n", TypeAsString(), name_);
110 : } else {
111 0 : base::OS::Print("\"");
112 0 : const char* c = name_;
113 0 : while (*c && (c - name_) <= 40) {
114 0 : if (*c != '\n')
115 0 : base::OS::Print("%c", *c);
116 : else
117 0 : base::OS::Print("\\n");
118 0 : ++c;
119 : }
120 0 : base::OS::Print("\"\n");
121 : }
122 0 : if (--max_depth == 0) return;
123 0 : for (auto i = children_begin(); i != children_end(); ++i) {
124 0 : HeapGraphEdge& edge = **i;
125 : const char* edge_prefix = "";
126 : EmbeddedVector<char, 64> index;
127 : const char* edge_name = index.start();
128 0 : switch (edge.type()) {
129 : case HeapGraphEdge::kContextVariable:
130 : edge_prefix = "#";
131 : edge_name = edge.name();
132 0 : break;
133 : case HeapGraphEdge::kElement:
134 0 : SNPrintF(index, "%d", edge.index());
135 0 : break;
136 : case HeapGraphEdge::kInternal:
137 : edge_prefix = "$";
138 : edge_name = edge.name();
139 0 : break;
140 : case HeapGraphEdge::kProperty:
141 : edge_name = edge.name();
142 0 : break;
143 : case HeapGraphEdge::kHidden:
144 : edge_prefix = "$";
145 0 : SNPrintF(index, "%d", edge.index());
146 0 : break;
147 : case HeapGraphEdge::kShortcut:
148 : edge_prefix = "^";
149 : edge_name = edge.name();
150 0 : break;
151 : case HeapGraphEdge::kWeak:
152 : edge_prefix = "w";
153 : edge_name = edge.name();
154 0 : break;
155 : default:
156 0 : SNPrintF(index, "!!! unknown edge type: %d ", edge.type());
157 : }
158 0 : edge.to()->Print(edge_prefix, edge_name, max_depth, indent + 2);
159 : }
160 : }
161 :
162 0 : const char* HeapEntry::TypeAsString() {
163 0 : switch (type()) {
164 : case kHidden: return "/hidden/";
165 0 : case kObject: return "/object/";
166 0 : case kClosure: return "/closure/";
167 0 : case kString: return "/string/";
168 0 : case kCode: return "/code/";
169 0 : case kArray: return "/array/";
170 0 : case kRegExp: return "/regexp/";
171 0 : case kHeapNumber: return "/number/";
172 0 : case kNative: return "/native/";
173 0 : case kSynthetic: return "/synthetic/";
174 0 : case kConsString: return "/concatenated string/";
175 0 : case kSlicedString: return "/sliced string/";
176 0 : case kSymbol: return "/symbol/";
177 : case kBigInt:
178 0 : return "/bigint/";
179 0 : default: return "???";
180 : }
181 : }
182 :
183 796 : HeapSnapshot::HeapSnapshot(HeapProfiler* profiler) : profiler_(profiler) {
184 : // It is very important to keep objects that form a heap snapshot
185 : // as small as possible. Check assumptions about data structure sizes.
186 : STATIC_ASSERT((kSystemPointerSize == 4 && sizeof(HeapGraphEdge) == 12) ||
187 : (kSystemPointerSize == 8 && sizeof(HeapGraphEdge) == 24));
188 : STATIC_ASSERT((kSystemPointerSize == 4 && sizeof(HeapEntry) == 28) ||
189 : (kSystemPointerSize == 8 && sizeof(HeapEntry) == 40));
190 398 : memset(&gc_subroot_entries_, 0, sizeof(gc_subroot_entries_));
191 398 : }
192 :
193 5 : void HeapSnapshot::Delete() {
194 5 : profiler_->RemoveSnapshot(this);
195 5 : }
196 :
197 0 : void HeapSnapshot::RememberLastJSObjectId() {
198 786 : max_snapshot_js_object_id_ = profiler_->heap_object_map()->last_assigned_id();
199 0 : }
200 :
201 398 : void HeapSnapshot::AddSyntheticRootEntries() {
202 : AddRootEntry();
203 : AddGcRootsEntry();
204 : SnapshotObjectId id = HeapObjectsMap::kGcRootsFirstSubrootId;
205 19502 : for (int root = 0; root < static_cast<int>(Root::kNumberOfRoots); root++) {
206 9552 : AddGcSubrootEntry(static_cast<Root>(root), id);
207 9552 : id += HeapObjectsMap::kObjectIdStep;
208 : }
209 : DCHECK_EQ(HeapObjectsMap::kFirstAvailableObjectId, id);
210 398 : }
211 :
212 0 : void HeapSnapshot::AddRootEntry() {
213 : DCHECK_NULL(root_entry_);
214 : DCHECK(entries_.empty()); // Root entry must be the first one.
215 398 : root_entry_ = AddEntry(HeapEntry::kSynthetic, "",
216 398 : HeapObjectsMap::kInternalRootObjectId, 0, 0);
217 : DCHECK_EQ(1u, entries_.size());
218 : DCHECK_EQ(root_entry_, &entries_.front());
219 0 : }
220 :
221 0 : void HeapSnapshot::AddGcRootsEntry() {
222 : DCHECK_NULL(gc_roots_entry_);
223 398 : gc_roots_entry_ = AddEntry(HeapEntry::kSynthetic, "(GC roots)",
224 398 : HeapObjectsMap::kGcRootsObjectId, 0, 0);
225 0 : }
226 :
227 9552 : void HeapSnapshot::AddGcSubrootEntry(Root root, SnapshotObjectId id) {
228 : DCHECK_NULL(gc_subroot_entries_[static_cast<int>(root)]);
229 : gc_subroot_entries_[static_cast<int>(root)] =
230 9552 : AddEntry(HeapEntry::kSynthetic, RootVisitor::RootName(root), id, 0, 0);
231 9552 : }
232 :
233 0 : void HeapSnapshot::AddLocation(HeapEntry* entry, int scriptId, int line,
234 : int col) {
235 134691 : locations_.emplace_back(entry->index(), scriptId, line, col);
236 0 : }
237 :
238 3113073 : HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
239 : const char* name,
240 : SnapshotObjectId id,
241 : size_t size,
242 : unsigned trace_node_id) {
243 : DCHECK(!is_complete());
244 6226146 : entries_.emplace_back(this, static_cast<int>(entries_.size()), type, name, id,
245 3113073 : size, trace_node_id);
246 3113073 : return &entries_.back();
247 : }
248 :
249 393 : void HeapSnapshot::FillChildren() {
250 : DCHECK(children().empty());
251 : int children_index = 0;
252 3097976 : for (HeapEntry& entry : entries()) {
253 : children_index = entry.set_children_index(children_index);
254 : }
255 : DCHECK_EQ(edges().size(), static_cast<size_t>(children_index));
256 393 : children().resize(edges().size());
257 10966366 : for (HeapGraphEdge& edge : edges()) {
258 : edge.from()->add_child(&edge);
259 : }
260 393 : }
261 :
262 183299 : HeapEntry* HeapSnapshot::GetEntryById(SnapshotObjectId id) {
263 183299 : if (entries_by_id_cache_.empty()) {
264 10 : CHECK(is_complete());
265 : entries_by_id_cache_.reserve(entries_.size());
266 67493 : for (HeapEntry& entry : entries_) {
267 134966 : entries_by_id_cache_.emplace(entry.id(), &entry);
268 : }
269 : }
270 : auto it = entries_by_id_cache_.find(id);
271 183299 : return it != entries_by_id_cache_.end() ? it->second : nullptr;
272 : }
273 :
274 0 : void HeapSnapshot::Print(int max_depth) {
275 0 : root()->Print("", "", max_depth, 0);
276 0 : }
277 :
278 : // We split IDs on evens for embedder objects (see
279 : // HeapObjectsMap::GenerateId) and odds for native objects.
280 : const SnapshotObjectId HeapObjectsMap::kInternalRootObjectId = 1;
281 : const SnapshotObjectId HeapObjectsMap::kGcRootsObjectId =
282 : HeapObjectsMap::kInternalRootObjectId + HeapObjectsMap::kObjectIdStep;
283 : const SnapshotObjectId HeapObjectsMap::kGcRootsFirstSubrootId =
284 : HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep;
285 : const SnapshotObjectId HeapObjectsMap::kFirstAvailableObjectId =
286 : HeapObjectsMap::kGcRootsFirstSubrootId +
287 : static_cast<int>(Root::kNumberOfRoots) * HeapObjectsMap::kObjectIdStep;
288 :
289 66326 : HeapObjectsMap::HeapObjectsMap(Heap* heap)
290 132652 : : next_id_(kFirstAvailableObjectId), heap_(heap) {
291 : // The dummy element at zero index is needed as entries_map_ cannot hold
292 : // an entry with zero value. Otherwise it's impossible to tell if
293 : // LookupOrInsert has added a new item or just returning exisiting one
294 : // having the value of zero.
295 66326 : entries_.emplace_back(0, kNullAddress, 0, true);
296 66326 : }
297 :
298 128397 : bool HeapObjectsMap::MoveObject(Address from, Address to, int object_size) {
299 : DCHECK_NE(kNullAddress, to);
300 : DCHECK_NE(kNullAddress, from);
301 128397 : if (from == to) return false;
302 256794 : void* from_value = entries_map_.Remove(reinterpret_cast<void*>(from),
303 128397 : ComputeAddressHash(from));
304 128397 : if (from_value == nullptr) {
305 : // It may occur that some untracked object moves to an address X and there
306 : // is a tracked object at that address. In this case we should remove the
307 : // entry as we know that the object has died.
308 253264 : void* to_value = entries_map_.Remove(reinterpret_cast<void*>(to),
309 126632 : ComputeAddressHash(to));
310 126632 : if (to_value != nullptr) {
311 : int to_entry_info_index =
312 2254 : static_cast<int>(reinterpret_cast<intptr_t>(to_value));
313 4508 : entries_.at(to_entry_info_index).addr = kNullAddress;
314 : }
315 : } else {
316 3530 : base::HashMap::Entry* to_entry = entries_map_.LookupOrInsert(
317 : reinterpret_cast<void*>(to), ComputeAddressHash(to));
318 1765 : if (to_entry->value != nullptr) {
319 : // We found the existing entry with to address for an old object.
320 : // Without this operation we will have two EntryInfo's with the same
321 : // value in addr field. It is bad because later at RemoveDeadEntries
322 : // one of this entry will be removed with the corresponding entries_map_
323 : // entry.
324 : int to_entry_info_index =
325 0 : static_cast<int>(reinterpret_cast<intptr_t>(to_entry->value));
326 0 : entries_.at(to_entry_info_index).addr = kNullAddress;
327 : }
328 : int from_entry_info_index =
329 1765 : static_cast<int>(reinterpret_cast<intptr_t>(from_value));
330 3530 : entries_.at(from_entry_info_index).addr = to;
331 : // Size of an object can change during its life, so to keep information
332 : // about the object in entries_ consistent, we have to adjust size when the
333 : // object is migrated.
334 1765 : if (FLAG_heap_profiler_trace_objects) {
335 0 : PrintF("Move object from %p to %p old size %6d new size %6d\n",
336 : reinterpret_cast<void*>(from), reinterpret_cast<void*>(to),
337 0 : entries_.at(from_entry_info_index).size, object_size);
338 : }
339 1765 : entries_.at(from_entry_info_index).size = object_size;
340 1765 : to_entry->value = from_value;
341 : }
342 128397 : return from_value != nullptr;
343 : }
344 :
345 :
346 0 : void HeapObjectsMap::UpdateObjectSize(Address addr, int size) {
347 0 : FindOrAddEntry(addr, size, false);
348 0 : }
349 :
350 :
351 919883 : SnapshotObjectId HeapObjectsMap::FindEntry(Address addr) {
352 919883 : base::HashMap::Entry* entry = entries_map_.Lookup(
353 : reinterpret_cast<void*>(addr), ComputeAddressHash(addr));
354 919883 : if (entry == nullptr) return 0;
355 591801 : int entry_index = static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
356 591801 : EntryInfo& entry_info = entries_.at(entry_index);
357 : DCHECK(static_cast<uint32_t>(entries_.size()) > entries_map_.occupancy());
358 591801 : return entry_info.id;
359 : }
360 :
361 :
362 3893283 : SnapshotObjectId HeapObjectsMap::FindOrAddEntry(Address addr,
363 : unsigned int size,
364 : bool accessed) {
365 : DCHECK(static_cast<uint32_t>(entries_.size()) > entries_map_.occupancy());
366 7786566 : base::HashMap::Entry* entry = entries_map_.LookupOrInsert(
367 : reinterpret_cast<void*>(addr), ComputeAddressHash(addr));
368 3893283 : if (entry->value != nullptr) {
369 : int entry_index =
370 976523 : static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
371 976523 : EntryInfo& entry_info = entries_.at(entry_index);
372 976523 : entry_info.accessed = accessed;
373 976523 : if (FLAG_heap_profiler_trace_objects) {
374 0 : PrintF("Update object size : %p with old size %d and new size %d\n",
375 0 : reinterpret_cast<void*>(addr), entry_info.size, size);
376 : }
377 976523 : entry_info.size = size;
378 976523 : return entry_info.id;
379 : }
380 2916760 : entry->value = reinterpret_cast<void*>(entries_.size());
381 2916760 : SnapshotObjectId id = next_id_;
382 2916760 : next_id_ += kObjectIdStep;
383 5833520 : entries_.push_back(EntryInfo(id, addr, size, accessed));
384 : DCHECK(static_cast<uint32_t>(entries_.size()) > entries_map_.occupancy());
385 2916760 : return id;
386 : }
387 :
388 7848 : void HeapObjectsMap::StopHeapObjectsTracking() { time_intervals_.clear(); }
389 :
390 100 : void HeapObjectsMap::UpdateHeapObjectsMap() {
391 100 : if (FLAG_heap_profiler_trace_objects) {
392 : PrintF("Begin HeapObjectsMap::UpdateHeapObjectsMap. map has %d entries.\n",
393 0 : entries_map_.occupancy());
394 : }
395 100 : heap_->PreciseCollectAllGarbage(Heap::kNoGCFlags,
396 100 : GarbageCollectionReason::kHeapProfiler);
397 200 : HeapIterator iterator(heap_);
398 1344050 : for (HeapObject obj = iterator.next(); !obj.is_null();
399 : obj = iterator.next()) {
400 1343850 : FindOrAddEntry(obj->address(), obj->Size());
401 671925 : if (FLAG_heap_profiler_trace_objects) {
402 0 : PrintF("Update object : %p %6d. Next address is %p\n",
403 : reinterpret_cast<void*>(obj->address()), obj->Size(),
404 0 : reinterpret_cast<void*>(obj->address() + obj->Size()));
405 : }
406 : }
407 100 : RemoveDeadEntries();
408 100 : if (FLAG_heap_profiler_trace_objects) {
409 : PrintF("End HeapObjectsMap::UpdateHeapObjectsMap. map has %d entries.\n",
410 0 : entries_map_.occupancy());
411 : }
412 100 : }
413 :
414 55 : SnapshotObjectId HeapObjectsMap::PushHeapObjectsStats(OutputStream* stream,
415 : int64_t* timestamp_us) {
416 55 : UpdateHeapObjectsMap();
417 55 : time_intervals_.emplace_back(next_id_);
418 55 : int prefered_chunk_size = stream->GetChunkSize();
419 : std::vector<v8::HeapStatsUpdate> stats_buffer;
420 : DCHECK(!entries_.empty());
421 : EntryInfo* entry_info = &entries_.front();
422 : EntryInfo* end_entry_info = &entries_.back() + 1;
423 615 : for (size_t time_interval_index = 0;
424 : time_interval_index < time_intervals_.size(); ++time_interval_index) {
425 : TimeInterval& time_interval = time_intervals_[time_interval_index];
426 280 : SnapshotObjectId time_interval_id = time_interval.id;
427 : uint32_t entries_size = 0;
428 : EntryInfo* start_entry_info = entry_info;
429 738566 : while (entry_info < end_entry_info && entry_info->id < time_interval_id) {
430 369143 : entries_size += entry_info->size;
431 369143 : ++entry_info;
432 : }
433 : uint32_t entries_count =
434 280 : static_cast<uint32_t>(entry_info - start_entry_info);
435 510 : if (time_interval.count != entries_count ||
436 230 : time_interval.size != entries_size) {
437 100 : stats_buffer.emplace_back(static_cast<uint32_t>(time_interval_index),
438 : time_interval.count = entries_count,
439 50 : time_interval.size = entries_size);
440 50 : if (static_cast<int>(stats_buffer.size()) >= prefered_chunk_size) {
441 : OutputStream::WriteResult result = stream->WriteHeapStatsChunk(
442 0 : &stats_buffer.front(), static_cast<int>(stats_buffer.size()));
443 0 : if (result == OutputStream::kAbort) return last_assigned_id();
444 : stats_buffer.clear();
445 : }
446 : }
447 : }
448 : DCHECK(entry_info == end_entry_info);
449 55 : if (!stats_buffer.empty()) {
450 45 : OutputStream::WriteResult result = stream->WriteHeapStatsChunk(
451 90 : &stats_buffer.front(), static_cast<int>(stats_buffer.size()));
452 45 : if (result == OutputStream::kAbort) return last_assigned_id();
453 : }
454 55 : stream->EndOfStream();
455 55 : if (timestamp_us) {
456 : *timestamp_us =
457 110 : (time_intervals_.back().timestamp - time_intervals_.front().timestamp)
458 55 : .InMicroseconds();
459 : }
460 55 : return last_assigned_id();
461 : }
462 :
463 :
464 498 : void HeapObjectsMap::RemoveDeadEntries() {
465 : DCHECK(entries_.size() > 0 && entries_.at(0).id == 0 &&
466 : entries_.at(0).addr == kNullAddress);
467 : size_t first_free_entry = 1;
468 7649876 : for (size_t i = 1; i < entries_.size(); ++i) {
469 : EntryInfo& entry_info = entries_.at(i);
470 3824689 : if (entry_info.accessed) {
471 3774570 : if (first_free_entry != i) {
472 91156 : entries_.at(first_free_entry) = entry_info;
473 : }
474 3774570 : entries_.at(first_free_entry).accessed = false;
475 : base::HashMap::Entry* entry =
476 7549140 : entries_map_.Lookup(reinterpret_cast<void*>(entry_info.addr),
477 : ComputeAddressHash(entry_info.addr));
478 : DCHECK(entry);
479 3774570 : entry->value = reinterpret_cast<void*>(first_free_entry);
480 3774570 : ++first_free_entry;
481 : } else {
482 50119 : if (entry_info.addr) {
483 95730 : entries_map_.Remove(reinterpret_cast<void*>(entry_info.addr),
484 47865 : ComputeAddressHash(entry_info.addr));
485 : }
486 : }
487 : }
488 498 : entries_.erase(entries_.begin() + first_free_entry, entries_.end());
489 :
490 : DCHECK(static_cast<uint32_t>(entries_.size()) - 1 ==
491 : entries_map_.occupancy());
492 498 : }
493 :
494 398 : V8HeapExplorer::V8HeapExplorer(HeapSnapshot* snapshot,
495 : SnapshottingProgressReportingInterface* progress,
496 : v8::HeapProfiler::ObjectNameResolver* resolver)
497 : : heap_(snapshot->profiler()->heap_object_map()->heap()),
498 : snapshot_(snapshot),
499 : names_(snapshot_->profiler()->names()),
500 : heap_object_map_(snapshot_->profiler()->heap_object_map()),
501 : progress_(progress),
502 : generator_(nullptr),
503 2388 : global_object_name_resolver_(resolver) {}
504 :
505 3102635 : HeapEntry* V8HeapExplorer::AllocateEntry(HeapThing ptr) {
506 3102635 : return AddEntry(HeapObject::cast(Object(reinterpret_cast<Address>(ptr))));
507 : }
508 :
509 3026852 : void V8HeapExplorer::ExtractLocation(HeapEntry* entry, HeapObject object) {
510 3026852 : if (object->IsJSFunction()) {
511 282524 : JSFunction func = JSFunction::cast(object);
512 282524 : ExtractLocationForJSFunction(entry, func);
513 :
514 2744328 : } else if (object->IsJSGeneratorObject()) {
515 : JSGeneratorObject gen = JSGeneratorObject::cast(object);
516 10 : ExtractLocationForJSFunction(entry, gen->function());
517 :
518 2744318 : } else if (object->IsJSObject()) {
519 141817 : JSObject obj = JSObject::cast(object);
520 141817 : JSFunction maybe_constructor = GetConstructor(obj);
521 :
522 141817 : if (!maybe_constructor.is_null()) {
523 109318 : ExtractLocationForJSFunction(entry, maybe_constructor);
524 : }
525 : }
526 3026852 : }
527 :
528 391852 : void V8HeapExplorer::ExtractLocationForJSFunction(HeapEntry* entry,
529 : JSFunction func) {
530 1040865 : if (!func->shared()->script()->IsScript()) return;
531 134691 : Script script = Script::cast(func->shared()->script());
532 : int scriptId = script->id();
533 134691 : int start = func->shared()->StartPosition();
534 134691 : int line = script->GetLineNumber(start);
535 134691 : int col = script->GetColumnNumber(start);
536 134691 : snapshot_->AddLocation(entry, scriptId, line, col);
537 : }
538 :
539 3102635 : HeapEntry* V8HeapExplorer::AddEntry(HeapObject object) {
540 3102635 : if (object->IsJSFunction()) {
541 : JSFunction func = JSFunction::cast(object);
542 282524 : SharedFunctionInfo shared = func->shared();
543 282524 : const char* name = names_->GetName(shared->Name());
544 282524 : return AddEntry(object, HeapEntry::kClosure, name);
545 2820111 : } else if (object->IsJSBoundFunction()) {
546 398 : return AddEntry(object, HeapEntry::kClosure, "native_bind");
547 2819713 : } else if (object->IsJSRegExp()) {
548 : JSRegExp re = JSRegExp::cast(object);
549 0 : return AddEntry(object,
550 : HeapEntry::kRegExp,
551 0 : names_->GetName(re->Pattern()));
552 2819713 : } else if (object->IsJSObject()) {
553 141434 : const char* name = names_->GetName(
554 282868 : GetConstructorName(JSObject::cast(object)));
555 141434 : if (object->IsJSGlobalObject()) {
556 796 : auto it = objects_tags_.find(JSGlobalObject::cast(object));
557 398 : if (it != objects_tags_.end()) {
558 30 : name = names_->GetFormatted("%s / %s", name, it->second);
559 : }
560 : }
561 141434 : return AddEntry(object, HeapEntry::kObject, name);
562 2678279 : } else if (object->IsString()) {
563 : String string = String::cast(object);
564 606389 : if (string->IsConsString()) {
565 120020 : return AddEntry(object, HeapEntry::kConsString, "(concatenated string)");
566 486369 : } else if (string->IsSlicedString()) {
567 5 : return AddEntry(object, HeapEntry::kSlicedString, "(sliced string)");
568 : } else {
569 486364 : return AddEntry(object, HeapEntry::kString,
570 972728 : names_->GetName(String::cast(object)));
571 : }
572 2071890 : } else if (object->IsSymbol()) {
573 17149 : if (Symbol::cast(object)->is_private())
574 11572 : return AddEntry(object, HeapEntry::kHidden, "private symbol");
575 : else
576 5577 : return AddEntry(object, HeapEntry::kSymbol, "symbol");
577 2054741 : } else if (object->IsBigInt()) {
578 10 : return AddEntry(object, HeapEntry::kBigInt, "bigint");
579 2054731 : } else if (object->IsCode()) {
580 607224 : return AddEntry(object, HeapEntry::kCode, "");
581 1447507 : } else if (object->IsSharedFunctionInfo()) {
582 293449 : String name = SharedFunctionInfo::cast(object)->Name();
583 293449 : return AddEntry(object, HeapEntry::kCode, names_->GetName(name));
584 1154058 : } else if (object->IsScript()) {
585 : Object name = Script::cast(object)->name();
586 1935 : return AddEntry(
587 : object, HeapEntry::kCode,
588 1935 : name->IsString() ? names_->GetName(String::cast(name)) : "");
589 1152566 : } else if (object->IsNativeContext()) {
590 398 : return AddEntry(object, HeapEntry::kHidden, "system / NativeContext");
591 1152168 : } else if (object->IsContext()) {
592 2040 : return AddEntry(object, HeapEntry::kObject, "system / Context");
593 3425443 : } else if (object->IsFixedArray() || object->IsFixedDoubleArray() ||
594 : object->IsByteArray()) {
595 16781 : return AddEntry(object, HeapEntry::kArray, "");
596 1133347 : } else if (object->IsHeapNumber()) {
597 14187 : return AddEntry(object, HeapEntry::kHeapNumber, "number");
598 : }
599 1119160 : return AddEntry(object, HeapEntry::kHidden, GetSystemEntryName(object));
600 : }
601 :
602 3102635 : HeapEntry* V8HeapExplorer::AddEntry(HeapObject object, HeapEntry::Type type,
603 : const char* name) {
604 6205270 : return AddEntry(object->address(), type, name, object->Size());
605 : }
606 :
607 3102645 : HeapEntry* V8HeapExplorer::AddEntry(Address address,
608 : HeapEntry::Type type,
609 : const char* name,
610 : size_t size) {
611 3102645 : SnapshotObjectId object_id = heap_object_map_->FindOrAddEntry(
612 3102645 : address, static_cast<unsigned int>(size));
613 : unsigned trace_node_id = 0;
614 3102645 : if (AllocationTracker* allocation_tracker =
615 3102645 : snapshot_->profiler()->allocation_tracker()) {
616 : trace_node_id =
617 33543 : allocation_tracker->address_to_trace()->GetTraceNodeId(address);
618 : }
619 3102645 : return snapshot_->AddEntry(type, name, object_id, size, trace_node_id);
620 : }
621 :
622 1119160 : const char* V8HeapExplorer::GetSystemEntryName(HeapObject object) {
623 1119160 : switch (object->map()->instance_type()) {
624 : case MAP_TYPE:
625 177445 : switch (Map::cast(object)->instance_type()) {
626 : #define MAKE_STRING_MAP_CASE(instance_type, size, name, Name) \
627 : case instance_type: return "system / Map (" #Name ")";
628 398 : STRING_TYPE_LIST(MAKE_STRING_MAP_CASE)
629 : #undef MAKE_STRING_MAP_CASE
630 169883 : default: return "system / Map";
631 : }
632 : case CELL_TYPE: return "system / Cell";
633 28896 : case PROPERTY_CELL_TYPE: return "system / PropertyCell";
634 27351 : case FOREIGN_TYPE: return "system / Foreign";
635 4776 : case ODDBALL_TYPE: return "system / Oddball";
636 : case ALLOCATION_SITE_TYPE:
637 16 : return "system / AllocationSite";
638 : #define MAKE_STRUCT_CASE(TYPE, Name, name) \
639 : case TYPE: \
640 : return "system / " #Name;
641 10 : STRUCT_LIST(MAKE_STRUCT_CASE)
642 : #undef MAKE_STRUCT_CASE
643 809780 : default: return "system";
644 : }
645 : }
646 :
647 15 : int V8HeapExplorer::EstimateObjectsCount() {
648 30 : HeapIterator it(heap_, HeapIterator::kFilterUnreachable);
649 : int objects_count = 0;
650 196494 : while (!it.next().is_null()) ++objects_count;
651 15 : return objects_count;
652 : }
653 :
654 6053704 : class IndexedReferencesExtractor : public ObjectVisitor {
655 : public:
656 : IndexedReferencesExtractor(V8HeapExplorer* generator, HeapObject parent_obj,
657 : HeapEntry* parent)
658 : : generator_(generator),
659 : parent_obj_(parent_obj),
660 : parent_start_(parent_obj_.RawMaybeWeakField(0)),
661 3026852 : parent_end_(parent_obj_.RawMaybeWeakField(parent_obj_->Size())),
662 : parent_(parent),
663 9080556 : next_index_(0) {}
664 6484337 : void VisitPointers(HeapObject host, ObjectSlot start,
665 : ObjectSlot end) override {
666 12968674 : VisitPointers(host, MaybeObjectSlot(start), MaybeObjectSlot(end));
667 6484337 : }
668 6734104 : 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 6734104 : CHECK_LE(parent_start_, start);
673 6734104 : CHECK_LE(end, parent_end_);
674 29633656 : for (MaybeObjectSlot p = start; p < end; ++p) {
675 16165448 : int field_index = static_cast<int>(p - parent_start_);
676 32330896 : if (generator_->visited_fields_[field_index]) {
677 : generator_->visited_fields_[field_index] = false;
678 : continue;
679 : }
680 : HeapObject heap_object;
681 3636677 : if ((*p)->GetHeapObject(&heap_object)) {
682 : VisitHeapObjectImpl(heap_object, field_index);
683 : }
684 : }
685 6734104 : }
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 1029 : void VisitEmbeddedPointer(Code host, RelocInfo* rinfo) override {
693 : VisitHeapObjectImpl(rinfo->target_object(), -1);
694 1029 : }
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 1903452 : generator_->SetHiddenReference(parent_obj_, parent_, next_index_++,
703 1903452 : 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 3026852 : void V8HeapExplorer::ExtractReferences(HeapEntry* entry, HeapObject obj) {
715 3026852 : if (obj->IsJSGlobalProxy()) {
716 : ExtractJSGlobalProxyReferences(entry, JSGlobalProxy::cast(obj));
717 3026459 : } else if (obj->IsJSArrayBuffer()) {
718 15 : ExtractJSArrayBufferReferences(entry, JSArrayBuffer::cast(obj));
719 3026444 : } else if (obj->IsJSObject()) {
720 423943 : if (obj->IsJSWeakSet()) {
721 : ExtractJSWeakCollectionReferences(entry, JSWeakSet::cast(obj));
722 423938 : } else if (obj->IsJSWeakMap()) {
723 : ExtractJSWeakCollectionReferences(entry, JSWeakMap::cast(obj));
724 423928 : } else if (obj->IsJSSet()) {
725 : ExtractJSCollectionReferences(entry, JSSet::cast(obj));
726 423923 : } else if (obj->IsJSMap()) {
727 : ExtractJSCollectionReferences(entry, JSMap::cast(obj));
728 423918 : } else if (obj->IsJSPromise()) {
729 : ExtractJSPromiseReferences(entry, JSPromise::cast(obj));
730 423893 : } else if (obj->IsJSGeneratorObject()) {
731 10 : ExtractJSGeneratorObjectReferences(entry, JSGeneratorObject::cast(obj));
732 : }
733 423943 : ExtractJSObjectReferences(entry, JSObject::cast(obj));
734 2602501 : } else if (obj->IsString()) {
735 603199 : ExtractStringReferences(entry, String::cast(obj));
736 1999302 : } else if (obj->IsSymbol()) {
737 : ExtractSymbolReferences(entry, Symbol::cast(obj));
738 1992700 : } else if (obj->IsMap()) {
739 137697 : ExtractMapReferences(entry, Map::cast(obj));
740 1855003 : } else if (obj->IsSharedFunctionInfo()) {
741 290344 : ExtractSharedFunctionInfoReferences(entry, SharedFunctionInfo::cast(obj));
742 1564659 : } else if (obj->IsScript()) {
743 1487 : ExtractScriptReferences(entry, Script::cast(obj));
744 1563172 : } else if (obj->IsAccessorInfo()) {
745 4323 : ExtractAccessorInfoReferences(entry, AccessorInfo::cast(obj));
746 1558849 : } else if (obj->IsAccessorPair()) {
747 28431 : ExtractAccessorPairReferences(entry, AccessorPair::cast(obj));
748 1530418 : } else if (obj->IsCode()) {
749 599609 : ExtractCodeReferences(entry, Code::cast(obj));
750 930809 : } else if (obj->IsCell()) {
751 : ExtractCellReferences(entry, Cell::cast(obj));
752 928132 : } else if (obj->IsFeedbackCell()) {
753 34485 : ExtractFeedbackCellReferences(entry, FeedbackCell::cast(obj));
754 893647 : } else if (obj->IsPropertyCell()) {
755 28438 : ExtractPropertyCellReferences(entry, PropertyCell::cast(obj));
756 865209 : } else if (obj->IsAllocationSite()) {
757 16 : ExtractAllocationSiteReferences(entry, AllocationSite::cast(obj));
758 865193 : } else if (obj->IsArrayBoilerplateDescription()) {
759 : ExtractArrayBoilerplateDescriptionReferences(
760 : entry, ArrayBoilerplateDescription::cast(obj));
761 865168 : } else if (obj->IsFeedbackVector()) {
762 448 : ExtractFeedbackVectorReferences(entry, FeedbackVector::cast(obj));
763 864720 : } else if (obj->IsDescriptorArray()) {
764 80777 : ExtractDescriptorArrayReferences(entry, DescriptorArray::cast(obj));
765 783943 : } else if (obj->IsWeakFixedArray()) {
766 : ExtractWeakArrayReferences(WeakFixedArray::kHeaderSize, entry,
767 5099 : WeakFixedArray::cast(obj));
768 778844 : } else if (obj->IsWeakArrayList()) {
769 : ExtractWeakArrayReferences(WeakArrayList::kHeaderSize, entry,
770 1514 : WeakArrayList::cast(obj));
771 777330 : } else if (obj->IsContext()) {
772 2433 : ExtractContextReferences(entry, Context::cast(obj));
773 774897 : } else if (obj->IsEphemeronHashTable()) {
774 15 : ExtractEphemeronHashTableReferences(entry, EphemeronHashTable::cast(obj));
775 774882 : } else if (obj->IsFixedArray()) {
776 10408 : ExtractFixedArrayReferences(entry, FixedArray::cast(obj));
777 : }
778 3026852 : }
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 423943 : void V8HeapExplorer::ExtractJSObjectReferences(HeapEntry* entry,
787 : JSObject js_obj) {
788 : HeapObject obj = js_obj;
789 423943 : ExtractPropertyReferences(js_obj, entry);
790 423943 : ExtractElementReferences(js_obj, entry);
791 423943 : ExtractInternalReferences(js_obj, entry);
792 423943 : Isolate* isolate = Isolate::FromHeap(heap_);
793 : PrototypeIterator iter(isolate, js_obj);
794 : ReadOnlyRoots roots(isolate);
795 847886 : SetPropertyReference(entry, roots.proto_string(), iter.GetCurrent());
796 423943 : if (obj->IsJSBoundFunction()) {
797 : JSBoundFunction js_fun = JSBoundFunction::cast(obj);
798 398 : TagObject(js_fun->bound_arguments(), "(bound arguments)");
799 796 : 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 796 : js_fun->bound_target_function(),
805 398 : JSBoundFunction::kBoundTargetFunctionOffset);
806 : FixedArray bindings = js_fun->bound_arguments();
807 418 : 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 423545 : } else if (obj->IsJSFunction()) {
812 282524 : JSFunction js_fun = JSFunction::cast(js_obj);
813 282524 : if (js_fun->has_prototype_slot()) {
814 : Object proto_or_map = js_fun->prototype_or_initial_map();
815 68682 : if (!proto_or_map->IsTheHole(isolate)) {
816 34774 : if (!proto_or_map->IsMap()) {
817 10 : SetPropertyReference(entry, roots.prototype_string(), proto_or_map,
818 : nullptr,
819 5 : JSFunction::kPrototypeOrInitialMapOffset);
820 : } else {
821 104307 : SetPropertyReference(entry, roots.prototype_string(),
822 34769 : js_fun->prototype());
823 : SetInternalReference(entry, "initial_map", proto_or_map,
824 34769 : JSFunction::kPrototypeOrInitialMapOffset);
825 : }
826 : }
827 : }
828 282524 : SharedFunctionInfo shared_info = js_fun->shared();
829 282524 : TagObject(js_fun->raw_feedback_cell(), "(function feedback cell)");
830 565048 : SetInternalReference(entry, "feedback_cell", js_fun->raw_feedback_cell(),
831 282524 : JSFunction::kFeedbackCellOffset);
832 282524 : TagObject(shared_info, "(shared function info)");
833 : SetInternalReference(entry, "shared", shared_info,
834 282524 : JSFunction::kSharedFunctionInfoOffset);
835 282524 : TagObject(js_fun->context(), "(context)");
836 565048 : SetInternalReference(entry, "context", js_fun->context(),
837 282524 : JSFunction::kContextOffset);
838 565048 : SetInternalReference(entry, "code", js_fun->code(),
839 282524 : JSFunction::kCodeOffset);
840 141021 : } else if (obj->IsJSGlobalObject()) {
841 : JSGlobalObject global_obj = JSGlobalObject::cast(obj);
842 786 : SetInternalReference(entry, "native_context", global_obj->native_context(),
843 393 : JSGlobalObject::kNativeContextOffset);
844 786 : SetInternalReference(entry, "global_proxy", global_obj->global_proxy(),
845 393 : JSGlobalObject::kGlobalProxyOffset);
846 : STATIC_ASSERT(JSGlobalObject::kSize - JSObject::kHeaderSize ==
847 : 2 * kTaggedSize);
848 140628 : } else if (obj->IsJSArrayBufferView()) {
849 : JSArrayBufferView view = JSArrayBufferView::cast(obj);
850 : SetInternalReference(entry, "buffer", view->buffer(),
851 5 : JSArrayBufferView::kBufferOffset);
852 : }
853 :
854 423943 : TagObject(js_obj->raw_properties_or_hash(), "(object properties)");
855 : SetInternalReference(entry, "properties", js_obj->raw_properties_or_hash(),
856 423943 : JSObject::kPropertiesOrHashOffset);
857 :
858 423943 : TagObject(js_obj->elements(), "(object elements)");
859 847886 : SetInternalReference(entry, "elements", js_obj->elements(),
860 423943 : JSObject::kElementsOffset);
861 423943 : }
862 :
863 603199 : void V8HeapExplorer::ExtractStringReferences(HeapEntry* entry, String string) {
864 603199 : if (string->IsConsString()) {
865 : ConsString cs = ConsString::cast(string);
866 120020 : SetInternalReference(entry, "first", cs->first(), ConsString::kFirstOffset);
867 240040 : SetInternalReference(entry, "second", cs->second(),
868 120020 : ConsString::kSecondOffset);
869 483179 : } else if (string->IsSlicedString()) {
870 : SlicedString ss = SlicedString::cast(string);
871 10 : SetInternalReference(entry, "parent", ss->parent(),
872 5 : SlicedString::kParentOffset);
873 483174 : } else if (string->IsThinString()) {
874 : ThinString ts = ThinString::cast(string);
875 0 : SetInternalReference(entry, "actual", ts->actual(),
876 0 : ThinString::kActualOffset);
877 : }
878 603199 : }
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 135 : 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 : Object key = table->get(key_index);
903 : 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 : HeapEntry* key_entry = GetEntry(key);
909 : 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 60 : 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 69184 : 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 438 : 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 189819 : for (size_t i = 0; i < arraysize(native_context_names); i++) {
969 94713 : int index = native_context_names[i].index;
970 94713 : const char* name = native_context_names[i].name;
971 : SetInternalReference(entry, name, context->get(index),
972 94713 : 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 137697 : void V8HeapExplorer::ExtractMapReferences(HeapEntry* entry, Map map) {
992 : MaybeObject maybe_raw_transitions_or_prototype_info = map->raw_transitions();
993 : HeapObject raw_transitions_or_prototype_info;
994 137697 : 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 137508 : } else if (maybe_raw_transitions_or_prototype_info->GetHeapObjectIfStrong(
1000 : &raw_transitions_or_prototype_info)) {
1001 26296 : if (raw_transitions_or_prototype_info->IsTransitionArray()) {
1002 : TransitionArray transitions =
1003 : TransitionArray::cast(raw_transitions_or_prototype_info);
1004 5512 : if (map->CanTransition() && transitions->HasPrototypeTransitions()) {
1005 786 : TagObject(transitions->GetPrototypeTransitions(),
1006 393 : "(prototype transitions)");
1007 : }
1008 2756 : TagObject(transitions, "(transition array)");
1009 : SetInternalReference(entry, "transitions", transitions,
1010 2756 : Map::kTransitionsOrPrototypeInfoOffset);
1011 47080 : } 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 23540 : } else if (map->is_prototype_map()) {
1018 23540 : TagObject(raw_transitions_or_prototype_info, "prototype_info");
1019 : SetInternalReference(entry, "prototype_info",
1020 : raw_transitions_or_prototype_info,
1021 23540 : Map::kTransitionsOrPrototypeInfoOffset);
1022 : }
1023 : }
1024 137697 : DescriptorArray descriptors = map->instance_descriptors();
1025 137697 : TagObject(descriptors, "(map descriptors)");
1026 : SetInternalReference(entry, "descriptors", descriptors,
1027 137697 : Map::kDescriptorsOffset);
1028 275394 : SetInternalReference(entry, "prototype", map->prototype(),
1029 137697 : Map::kPrototypeOffset);
1030 : if (FLAG_unbox_double_fields) {
1031 275394 : SetInternalReference(entry, "layout_descriptor", map->layout_descriptor(),
1032 137697 : Map::kLayoutDescriptorOffset);
1033 : }
1034 : Object constructor_or_backpointer = map->constructor_or_backpointer();
1035 137697 : 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 135533 : } 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 135533 : Map::kConstructorOrBackPointerOffset);
1047 : }
1048 137697 : TagObject(map->dependent_code(), "(dependent code)");
1049 275394 : SetInternalReference(entry, "dependent_code", map->dependent_code(),
1050 137697 : Map::kDependentCodeOffset);
1051 137697 : }
1052 :
1053 290344 : void V8HeapExplorer::ExtractSharedFunctionInfoReferences(
1054 : HeapEntry* entry, SharedFunctionInfo shared) {
1055 290344 : String shared_name = shared->DebugName();
1056 : const char* name = nullptr;
1057 580688 : if (shared_name != ReadOnlyRoots(heap_).empty_string()) {
1058 270719 : name = names_->GetName(shared_name);
1059 270719 : TagObject(shared->GetCode(), names_->GetFormatted("(code for %s)", name));
1060 : } else {
1061 58875 : TagObject(shared->GetCode(),
1062 19625 : names_->GetFormatted(
1063 58875 : "(%s code)", Code::Kind2String(shared->GetCode()->kind())));
1064 : }
1065 :
1066 290344 : 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 290344 : SharedFunctionInfo::kNameOrScopeInfoOffset);
1072 : SetInternalReference(entry, "script_or_debug_info",
1073 : shared->script_or_debug_info(),
1074 290344 : SharedFunctionInfo::kScriptOrDebugInfoOffset);
1075 : SetInternalReference(entry, "function_data", shared->function_data(),
1076 290344 : SharedFunctionInfo::kFunctionDataOffset);
1077 : SetInternalReference(
1078 : entry, "raw_outer_scope_info_or_feedback_metadata",
1079 580688 : shared->raw_outer_scope_info_or_feedback_metadata(),
1080 290344 : SharedFunctionInfo::kOuterScopeInfoOrFeedbackMetadataOffset);
1081 290344 : }
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 8646 : 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 28431 : void V8HeapExplorer::ExtractAccessorPairReferences(HeapEntry* entry,
1110 : AccessorPair accessors) {
1111 : SetInternalReference(entry, "getter", accessors->getter(),
1112 28431 : AccessorPair::kGetterOffset);
1113 : SetInternalReference(entry, "setter", accessors->setter(),
1114 28431 : AccessorPair::kSetterOffset);
1115 28431 : }
1116 :
1117 605756 : void V8HeapExplorer::TagBuiltinCodeObject(Code code, const char* name) {
1118 605756 : TagObject(code, names_->GetFormatted("(%s builtin)", name));
1119 605756 : }
1120 :
1121 599609 : void V8HeapExplorer::ExtractCodeReferences(HeapEntry* entry, Code code) {
1122 599609 : TagObject(code->relocation_info(), "(code relocation info)");
1123 1199218 : SetInternalReference(entry, "relocation_info", code->relocation_info(),
1124 599609 : Code::kRelocationInfoOffset);
1125 599609 : TagObject(code->deoptimization_data(), "(code deopt data)");
1126 : SetInternalReference(entry, "deoptimization_data",
1127 1199218 : code->deoptimization_data(),
1128 599609 : Code::kDeoptimizationDataOffset);
1129 599609 : TagObject(code->source_position_table(), "(source position table)");
1130 : SetInternalReference(entry, "source_position_table",
1131 : code->source_position_table(),
1132 599609 : Code::kSourcePositionTableOffset);
1133 599609 : }
1134 :
1135 0 : void V8HeapExplorer::ExtractCellReferences(HeapEntry* entry, Cell cell) {
1136 2677 : SetInternalReference(entry, "value", cell->value(), Cell::kValueOffset);
1137 0 : }
1138 :
1139 34485 : void V8HeapExplorer::ExtractFeedbackCellReferences(HeapEntry* entry,
1140 : FeedbackCell feedback_cell) {
1141 34485 : TagObject(feedback_cell, "(feedback cell)");
1142 68970 : SetInternalReference(entry, "value", feedback_cell->value(),
1143 34485 : FeedbackCell::kValueOffset);
1144 34485 : }
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 56876 : 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 32 : SetInternalReference(entry, "dependent_code", site->dependent_code(),
1164 16 : AllocationSite::kDependentCodeOffset);
1165 16 : }
1166 :
1167 0 : void V8HeapExplorer::ExtractArrayBoilerplateDescriptionReferences(
1168 : HeapEntry* entry, ArrayBoilerplateDescription value) {
1169 50 : SetInternalReference(entry, "constant_elements", value->constant_elements(),
1170 25 : ArrayBoilerplateDescription::kConstantElementsOffset);
1171 0 : }
1172 :
1173 15 : 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 10 : 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 20 : SetInternalReference(entry, "function", generator->function(),
1211 10 : JSGeneratorObject::kFunctionOffset);
1212 20 : 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 20 : generator->parameters_and_registers(),
1218 10 : JSGeneratorObject::kParametersAndRegistersOffset);
1219 10 : }
1220 :
1221 10408 : void V8HeapExplorer::ExtractFixedArrayReferences(HeapEntry* entry,
1222 : FixedArray array) {
1223 5668204 : for (int i = 0, l = array->length(); i < l; ++i) {
1224 : DCHECK(!HasWeakHeapObjectTag(array->get(i)));
1225 2828898 : SetInternalReference(entry, i, array->get(i), array->OffsetOfElementAt(i));
1226 : }
1227 10408 : }
1228 :
1229 448 : void V8HeapExplorer::ExtractFeedbackVectorReferences(
1230 : HeapEntry* entry, FeedbackVector feedback_vector) {
1231 : 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 80777 : void V8HeapExplorer::ExtractDescriptorArrayReferences(HeapEntry* entry,
1240 : DescriptorArray array) {
1241 161554 : SetInternalReference(entry, "enum_cache", array->enum_cache(),
1242 80777 : DescriptorArray::kEnumCacheOffset);
1243 : MaybeObjectSlot start = MaybeObjectSlot(array->GetDescriptorSlot(0));
1244 : MaybeObjectSlot end = MaybeObjectSlot(
1245 80777 : array->GetDescriptorSlot(array->number_of_all_descriptors()));
1246 2708921 : for (int i = 0; start + i < end; ++i) {
1247 : MaybeObjectSlot slot = start + i;
1248 2628144 : int offset = static_cast<int>(slot.address() - array->address());
1249 : MaybeObject object = *slot;
1250 1314072 : HeapObject heap_object;
1251 1314072 : if (object->GetHeapObjectIfWeak(&heap_object)) {
1252 4167 : SetWeakReference(entry, i, heap_object, offset);
1253 1309905 : } else if (object->GetHeapObjectIfStrong(&heap_object)) {
1254 566545 : SetInternalReference(entry, i, heap_object, offset);
1255 : }
1256 : }
1257 80777 : }
1258 :
1259 : template <typename T>
1260 6613 : void V8HeapExplorer::ExtractWeakArrayReferences(int header_size,
1261 : HeapEntry* entry, T array) {
1262 680243 : for (int i = 0; i < array->length(); ++i) {
1263 : MaybeObject object = array->Get(i);
1264 336815 : HeapObject heap_object;
1265 336815 : if (object->GetHeapObjectIfWeak(&heap_object)) {
1266 294241 : SetWeakReference(entry, i, heap_object, header_size + i * kTaggedSize);
1267 42574 : } else if (object->GetHeapObjectIfStrong(&heap_object)) {
1268 35306 : SetInternalReference(entry, i, heap_object,
1269 : header_size + i * kTaggedSize);
1270 : }
1271 : }
1272 6613 : }
1273 :
1274 423943 : void V8HeapExplorer::ExtractPropertyReferences(JSObject js_obj,
1275 : HeapEntry* entry) {
1276 : Isolate* isolate = js_obj->GetIsolate();
1277 423943 : if (js_obj->HasFastProperties()) {
1278 423370 : DescriptorArray descs = js_obj->map()->instance_descriptors();
1279 : int real_size = js_obj->map()->NumberOfOwnDescriptors();
1280 2489716 : for (int i = 0; i < real_size; i++) {
1281 1033173 : PropertyDetails details = descs->GetDetails(i);
1282 1033173 : switch (details.location()) {
1283 : case kField: {
1284 : Representation r = details.representation();
1285 294706 : if (r.IsSmi() || r.IsDouble()) break;
1286 :
1287 266386 : Name k = descs->GetKey(i);
1288 266386 : FieldIndex field_index = FieldIndex::ForDescriptor(js_obj->map(), i);
1289 266386 : Object value = js_obj->RawFastPropertyAt(field_index);
1290 : int field_offset =
1291 266386 : field_index.is_inobject() ? field_index.offset() : -1;
1292 :
1293 : SetDataOrAccessorPropertyReference(details.kind(), entry, k, value,
1294 266386 : nullptr, field_offset);
1295 266386 : break;
1296 : }
1297 : case kDescriptor:
1298 : SetDataOrAccessorPropertyReference(details.kind(), entry,
1299 : descs->GetKey(i),
1300 738467 : descs->GetStrongValue(i));
1301 738467 : 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 : JSGlobalObject::cast(js_obj)->global_dictionary();
1308 : int length = dictionary->Capacity();
1309 : ReadOnlyRoots roots(isolate);
1310 101001 : for (int i = 0; i < length; ++i) {
1311 76886 : if (!dictionary->IsKey(roots, dictionary->KeyAt(i))) continue;
1312 : PropertyCell cell = dictionary->CellAt(i);
1313 23722 : Name name = cell->name();
1314 23722 : Object value = cell->value();
1315 : 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 : int length = dictionary->Capacity();
1321 : ReadOnlyRoots roots(isolate);
1322 4380 : for (int i = 0; i < length; ++i) {
1323 : 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 423943 : }
1332 :
1333 738482 : void V8HeapExplorer::ExtractAccessorPairProperty(HeapEntry* entry, Name key,
1334 : Object callback_obj,
1335 : int field_offset) {
1336 738482 : if (!callback_obj->IsAccessorPair()) return;
1337 : AccessorPair accessors = AccessorPair::cast(callback_obj);
1338 28431 : SetPropertyReference(entry, key, accessors, nullptr, field_offset);
1339 : Object getter = accessors->getter();
1340 28431 : if (!getter->IsOddball()) {
1341 28421 : SetPropertyReference(entry, key, getter, "get %s");
1342 : }
1343 : Object setter = accessors->setter();
1344 28431 : if (!setter->IsOddball()) {
1345 9002 : SetPropertyReference(entry, key, setter, "set %s");
1346 : }
1347 : }
1348 :
1349 423943 : void V8HeapExplorer::ExtractElementReferences(JSObject js_obj,
1350 : HeapEntry* entry) {
1351 : ReadOnlyRoots roots = js_obj->GetReadOnlyRoots();
1352 423943 : if (js_obj->HasObjectElements()) {
1353 : FixedArray elements = FixedArray::cast(js_obj->elements());
1354 : int length = js_obj->IsJSArray()
1355 : ? Smi::ToInt(JSArray::cast(js_obj)->length())
1356 423152 : : elements->length();
1357 683594 : for (int i = 0; i < length; ++i) {
1358 130221 : if (!elements->get(i)->IsTheHole(roots)) {
1359 130131 : SetElementReference(entry, i, elements->get(i));
1360 : }
1361 : }
1362 791 : } else if (js_obj->HasDictionaryElements()) {
1363 : NumberDictionary dictionary = js_obj->element_dictionary();
1364 : int length = dictionary->Capacity();
1365 1179 : for (int i = 0; i < length; ++i) {
1366 : Object k = dictionary->KeyAt(i);
1367 393 : 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 423943 : }
1374 :
1375 423943 : void V8HeapExplorer::ExtractInternalReferences(JSObject js_obj,
1376 : HeapEntry* entry) {
1377 423943 : int length = js_obj->GetEmbedderFieldCount();
1378 424133 : 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 423943 : }
1383 :
1384 141847 : JSFunction V8HeapExplorer::GetConstructor(JSReceiver receiver) {
1385 : Isolate* isolate = receiver->GetIsolate();
1386 : DisallowHeapAllocation no_gc;
1387 : HandleScope scope(isolate);
1388 : MaybeHandle<JSFunction> maybe_constructor =
1389 141847 : JSReceiver::GetConstructor(handle(receiver, isolate));
1390 :
1391 141847 : if (maybe_constructor.is_null()) return JSFunction();
1392 :
1393 : return *maybe_constructor.ToHandleChecked();
1394 : }
1395 :
1396 141464 : String V8HeapExplorer::GetConstructorName(JSObject object) {
1397 : Isolate* isolate = object->GetIsolate();
1398 141464 : if (object->IsJSFunction()) return ReadOnlyRoots(isolate).closure_string();
1399 : DisallowHeapAllocation no_gc;
1400 : HandleScope scope(isolate);
1401 282928 : return *JSReceiver::GetConstructorName(handle(object, isolate));
1402 : }
1403 :
1404 0 : HeapEntry* V8HeapExplorer::GetEntry(Object obj) {
1405 22306538 : return obj->IsHeapObject() ? generator_->FindOrAddEntry(
1406 : reinterpret_cast<void*>(obj.ptr()), this)
1407 45279036 : : nullptr;
1408 : }
1409 :
1410 398 : 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 1453058 : void VisitRootPointer(Root root, const char* description,
1418 : FullObjectSlot object) override {
1419 1453058 : if (root == Root::kBuiltins) {
1420 605756 : explorer_->TagBuiltinCodeObject(Code::cast(*object), description);
1421 : }
1422 1453058 : explorer_->SetGcSubrootReference(root, description, visiting_weak_roots_,
1423 1453058 : *object);
1424 1453058 : }
1425 :
1426 4318 : void VisitRootPointers(Root root, const char* description,
1427 : FullObjectSlot start, FullObjectSlot end) override {
1428 253857 : for (FullObjectSlot p = start; p < end; ++p) {
1429 245221 : 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 19502 : 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 9179569 : for (HeapObject obj = iterator.next(); !obj.is_null();
1462 3059591 : obj = iterator.next(), progress_->ProgressStep()) {
1463 3092330 : if (interrupted) continue;
1464 :
1465 3026852 : size_t max_pointer = obj->Size() / kTaggedSize;
1466 3026852 : if (max_pointer > visited_fields_.size()) {
1467 : // Clear the current bits.
1468 2368 : std::vector<bool>().swap(visited_fields_);
1469 : // Reallocate to right size.
1470 2368 : visited_fields_.resize(max_pointer, false);
1471 : }
1472 :
1473 : HeapEntry* entry = GetEntry(obj);
1474 3026852 : ExtractReferences(entry, obj);
1475 3026852 : SetInternalReference(entry, "map", obj->map(), HeapObject::kMapOffset);
1476 : // Extract unvisited fields as hidden references and restore tags
1477 : // of visited fields.
1478 : IndexedReferencesExtractor refs_extractor(this, obj, entry);
1479 3026852 : 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 3026852 : ExtractLocation(entry, obj);
1488 :
1489 3026852 : if (!progress_->ProgressReport(false)) interrupted = true;
1490 : }
1491 :
1492 398 : generator_ = nullptr;
1493 796 : return interrupted ? false : progress_->ProgressReport(true);
1494 : }
1495 :
1496 19065031 : bool V8HeapExplorer::IsEssentialObject(Object object) {
1497 19065031 : ReadOnlyRoots roots(heap_);
1498 34914448 : return object->IsHeapObject() && !object->IsOddball() &&
1499 14650315 : object != roots.empty_byte_array() &&
1500 11872830 : object != roots.empty_fixed_array() &&
1501 11540168 : object != roots.empty_weak_fixed_array() &&
1502 11429492 : object != roots.empty_descriptor_array() &&
1503 22846605 : object != roots.fixed_array_map() && object != roots.cell_map() &&
1504 11393526 : object != roots.global_property_cell_map() &&
1505 11103182 : object != roots.shared_function_info_map() &&
1506 11103182 : object != roots.free_space_map() &&
1507 30166983 : object != roots.one_pointer_filler_map() &&
1508 19065031 : object != roots.two_pointer_filler_map();
1509 : }
1510 :
1511 1000324 : bool V8HeapExplorer::IsEssentialHiddenReference(Object parent,
1512 : int field_offset) {
1513 1000324 : if (parent->IsAllocationSite() &&
1514 : field_offset == AllocationSite::kWeakNextOffset)
1515 : return false;
1516 1000324 : if (parent->IsCodeDataContainer() &&
1517 : field_offset == CodeDataContainer::kNextCodeLinkOffset)
1518 : return false;
1519 1000288 : if (parent->IsContext() &&
1520 : field_offset == Context::OffsetOfElementAt(Context::NEXT_CONTEXT_LINK))
1521 : return false;
1522 1000288 : return true;
1523 : }
1524 :
1525 33587 : void V8HeapExplorer::SetContextReference(HeapEntry* parent_entry,
1526 : String reference_name,
1527 : Object child_obj, int field_offset) {
1528 : HeapEntry* child_entry = GetEntry(child_obj);
1529 33587 : if (child_entry == nullptr) return;
1530 33577 : 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 13243876 : if (offset < 0) return;
1537 12528771 : int index = offset / kTaggedSize;
1538 : DCHECK(!visited_fields_[index]);
1539 12528771 : visited_fields_[index] = true;
1540 : }
1541 :
1542 10 : void V8HeapExplorer::SetNativeBindReference(HeapEntry* parent_entry,
1543 : const char* reference_name,
1544 : Object child_obj) {
1545 : HeapEntry* child_entry = GetEntry(child_obj);
1546 10 : 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 : HeapEntry* child_entry = GetEntry(child_obj);
1554 130131 : if (child_entry == nullptr) return;
1555 : parent_entry->SetIndexedReference(HeapGraphEdge::kElement, index,
1556 : child_entry);
1557 : }
1558 :
1559 9327460 : void V8HeapExplorer::SetInternalReference(HeapEntry* parent_entry,
1560 : const char* reference_name,
1561 : Object child_obj, int field_offset) {
1562 : HeapEntry* child_entry = GetEntry(child_obj);
1563 9327460 : if (child_entry == nullptr) return;
1564 8932002 : if (IsEssentialObject(child_obj)) {
1565 : parent_entry->SetNamedReference(HeapGraphEdge::kInternal, reference_name,
1566 : child_entry);
1567 : }
1568 : MarkVisitedField(field_offset);
1569 : }
1570 :
1571 3430844 : void V8HeapExplorer::SetInternalReference(HeapEntry* parent_entry, int index,
1572 : Object child_obj, int field_offset) {
1573 : HeapEntry* child_entry = GetEntry(child_obj);
1574 3430844 : if (child_entry == nullptr) return;
1575 3168978 : if (IsEssentialObject(child_obj)) {
1576 1244382 : parent_entry->SetNamedReference(HeapGraphEdge::kInternal,
1577 1244382 : names_->GetName(index), child_entry);
1578 : }
1579 : MarkVisitedField(field_offset);
1580 : }
1581 :
1582 1903452 : 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 : HeapEntry* child_entry = GetEntry(child_obj);
1587 2903776 : if (child_entry != nullptr && IsEssentialObject(child_obj) &&
1588 1000324 : IsEssentialHiddenReference(parent_obj, field_offset)) {
1589 : parent_entry->SetIndexedReference(HeapGraphEdge::kHidden, index,
1590 : child_entry);
1591 : }
1592 1903452 : }
1593 :
1594 979 : void V8HeapExplorer::SetWeakReference(HeapEntry* parent_entry,
1595 : const char* reference_name,
1596 : Object child_obj, int field_offset) {
1597 : HeapEntry* child_entry = GetEntry(child_obj);
1598 979 : 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 298528 : void V8HeapExplorer::SetWeakReference(HeapEntry* parent_entry, int index,
1607 : Object child_obj, int field_offset) {
1608 : HeapEntry* child_entry = GetEntry(child_obj);
1609 298528 : if (child_entry == nullptr) return;
1610 298528 : if (IsEssentialObject(child_obj)) {
1611 298438 : parent_entry->SetNamedReference(
1612 298438 : HeapGraphEdge::kWeak, names_->GetFormatted("%d", index), child_entry);
1613 : }
1614 : MarkVisitedField(field_offset);
1615 : }
1616 :
1617 1029265 : 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 1029265 : if (kind == kAccessor) {
1621 : ExtractAccessorPairProperty(parent_entry, reference_name, child_obj,
1622 738482 : field_offset);
1623 : } else {
1624 : SetPropertyReference(parent_entry, reference_name, child_obj,
1625 290783 : name_format_string, field_offset);
1626 : }
1627 1029265 : }
1628 :
1629 815354 : void V8HeapExplorer::SetPropertyReference(HeapEntry* parent_entry,
1630 : Name reference_name, Object child_obj,
1631 : const char* name_format_string,
1632 : int field_offset) {
1633 : HeapEntry* child_entry = GetEntry(child_obj);
1634 815354 : if (child_entry == nullptr) return;
1635 : HeapGraphEdge::Type type =
1636 781550 : reference_name->IsSymbol() || String::cast(reference_name)->length() > 0
1637 : ? HeapGraphEdge::kProperty
1638 809812 : : HeapGraphEdge::kInternal;
1639 : const char* name =
1640 37423 : name_format_string != nullptr && reference_name->IsString()
1641 33886 : ? names_->GetFormatted(
1642 : name_format_string,
1643 877584 : String::cast(reference_name)
1644 877584 : ->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL)
1645 : .get())
1646 1619624 : : 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 : HeapEntry* child_entry = GetEntry(child_obj);
1659 : DCHECK_NOT_NULL(child_entry);
1660 398 : 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 1453058 : void V8HeapExplorer::SetGcSubrootReference(Root root, const char* description,
1670 : bool is_weak, Object child_obj) {
1671 : HeapEntry* child_entry = GetEntry(child_obj);
1672 2905718 : if (child_entry == nullptr) return;
1673 1449987 : const char* name = GetStrongGcSubrootName(child_obj);
1674 : HeapGraphEdge::Type edge_type =
1675 1449987 : is_weak ? HeapGraphEdge::kWeak : HeapGraphEdge::kInternal;
1676 1449987 : if (name != nullptr) {
1677 239062 : snapshot_->gc_subroot(root)->SetNamedReference(edge_type, name,
1678 : child_entry);
1679 : } else {
1680 1210925 : snapshot_->gc_subroot(root)->SetNamedAutoIndexReference(
1681 1210925 : edge_type, description, child_entry, names_);
1682 : }
1683 :
1684 : // For full heap snapshots we do not emit user roots but rather rely on
1685 : // regular GC roots to retain objects.
1686 : if (FLAG_raw_heap_snapshots) return;
1687 :
1688 : // Add a shortcut to JS global object reference at snapshot root.
1689 : // That allows the user to easily find global objects. They are
1690 : // also used as starting points in distance calculations.
1691 2899919 : if (is_weak || !child_obj->IsNativeContext()) return;
1692 :
1693 1952 : JSGlobalObject global = Context::cast(child_obj)->global_object();
1694 1952 : if (!global->IsJSGlobalObject()) return;
1695 :
1696 1952 : if (!user_roots_.insert(global).second) return;
1697 :
1698 398 : SetUserGlobalReference(global);
1699 : }
1700 :
1701 1449987 : const char* V8HeapExplorer::GetStrongGcSubrootName(Object object) {
1702 1449987 : if (strong_gc_subroot_names_.empty()) {
1703 398 : Isolate* isolate = Isolate::FromHeap(heap_);
1704 : for (RootIndex root_index = RootIndex::kFirstStrongOrReadOnlyRoot;
1705 235218 : root_index <= RootIndex::kLastStrongOrReadOnlyRoot; ++root_index) {
1706 234820 : const char* name = RootsTable::name(root_index);
1707 469640 : strong_gc_subroot_names_.emplace(isolate->root(root_index), name);
1708 : }
1709 398 : CHECK(!strong_gc_subroot_names_.empty());
1710 : }
1711 : auto it = strong_gc_subroot_names_.find(object);
1712 1449987 : return it != strong_gc_subroot_names_.end() ? it->second : nullptr;
1713 : }
1714 :
1715 4761092 : void V8HeapExplorer::TagObject(Object obj, const char* tag) {
1716 4761092 : if (IsEssentialObject(obj)) {
1717 : HeapEntry* entry = GetEntry(obj);
1718 2551725 : if (entry->name()[0] == '\0') {
1719 : entry->set_name(tag);
1720 : }
1721 : }
1722 4761092 : }
1723 :
1724 1194 : class GlobalObjectsEnumerator : public RootVisitor {
1725 : public:
1726 603 : void VisitRootPointers(Root root, const char* description,
1727 : FullObjectSlot start, FullObjectSlot end) override {
1728 1809 : for (FullObjectSlot p = start; p < end; ++p) {
1729 603 : if (!(*p)->IsNativeContext()) continue;
1730 423 : JSObject proxy = Context::cast(*p)->global_proxy();
1731 423 : if (!proxy->IsJSGlobalProxy()) continue;
1732 : Object global = proxy->map()->prototype();
1733 423 : if (!global->IsJSGlobalObject()) continue;
1734 846 : objects_.push_back(Handle<JSGlobalObject>(JSGlobalObject::cast(global),
1735 : proxy->GetIsolate()));
1736 : }
1737 603 : }
1738 1194 : int count() const { return static_cast<int>(objects_.size()); }
1739 : Handle<JSGlobalObject>& at(int i) { return objects_[i]; }
1740 :
1741 : private:
1742 : std::vector<Handle<JSGlobalObject>> objects_;
1743 : };
1744 :
1745 :
1746 : // Modifies heap. Must not be run during heap traversal.
1747 398 : void V8HeapExplorer::TagGlobalObjects() {
1748 398 : Isolate* isolate = Isolate::FromHeap(heap_);
1749 : HandleScope scope(isolate);
1750 : GlobalObjectsEnumerator enumerator;
1751 398 : isolate->global_handles()->IterateAllRoots(&enumerator);
1752 398 : std::vector<const char*> urls(enumerator.count());
1753 1244 : for (int i = 0, l = enumerator.count(); i < l; ++i) {
1754 846 : urls[i] = global_object_name_resolver_
1755 : ? global_object_name_resolver_->GetName(Utils::ToLocal(
1756 55 : Handle<JSObject>::cast(enumerator.at(i))))
1757 478 : : nullptr;
1758 : }
1759 :
1760 : DisallowHeapAllocation no_allocation;
1761 1244 : for (int i = 0, l = enumerator.count(); i < l; ++i) {
1762 956 : if (urls[i]) objects_tags_.emplace(*enumerator.at(i), urls[i]);
1763 : }
1764 398 : }
1765 :
1766 140 : class EmbedderGraphImpl : public EmbedderGraph {
1767 : public:
1768 : struct Edge {
1769 : Node* from;
1770 : Node* to;
1771 : const char* name;
1772 : };
1773 :
1774 100 : class V8NodeImpl : public Node {
1775 : public:
1776 50 : explicit V8NodeImpl(Object object) : object_(object) {}
1777 : Object GetObject() { return object_; }
1778 :
1779 : // Node overrides.
1780 75 : bool IsEmbedderNode() override { return false; }
1781 0 : const char* Name() override {
1782 : // The name should be retrieved via GetObject().
1783 0 : UNREACHABLE();
1784 : return "";
1785 : }
1786 0 : size_t SizeInBytes() override {
1787 : // The size should be retrieved via GetObject().
1788 0 : UNREACHABLE();
1789 : return 0;
1790 : }
1791 :
1792 : private:
1793 : Object object_;
1794 : };
1795 :
1796 50 : Node* V8Node(const v8::Local<v8::Value>& value) final {
1797 : Handle<Object> object = v8::Utils::OpenHandle(*value);
1798 : DCHECK(!object.is_null());
1799 100 : return AddNode(std::unique_ptr<Node>(new V8NodeImpl(*object)));
1800 : }
1801 :
1802 90 : Node* AddNode(std::unique_ptr<Node> node) final {
1803 : Node* result = node.get();
1804 140 : nodes_.push_back(std::move(node));
1805 90 : return result;
1806 : }
1807 :
1808 110 : void AddEdge(Node* from, Node* to, const char* name) final {
1809 220 : edges_.push_back({from, to, name});
1810 110 : }
1811 :
1812 : const std::vector<std::unique_ptr<Node>>& nodes() { return nodes_; }
1813 : const std::vector<Edge>& edges() { return edges_; }
1814 :
1815 : private:
1816 : std::vector<std::unique_ptr<Node>> nodes_;
1817 : std::vector<Edge> edges_;
1818 : };
1819 :
1820 796 : class EmbedderGraphEntriesAllocator : public HeapEntriesAllocator {
1821 : public:
1822 : explicit EmbedderGraphEntriesAllocator(HeapSnapshot* snapshot)
1823 : : snapshot_(snapshot),
1824 : names_(snapshot_->profiler()->names()),
1825 1194 : heap_object_map_(snapshot_->profiler()->heap_object_map()) {}
1826 : HeapEntry* AllocateEntry(HeapThing ptr) override;
1827 :
1828 : private:
1829 : HeapSnapshot* snapshot_;
1830 : StringsStorage* names_;
1831 : HeapObjectsMap* heap_object_map_;
1832 : };
1833 :
1834 : namespace {
1835 :
1836 90 : const char* EmbedderGraphNodeName(StringsStorage* names,
1837 : EmbedderGraphImpl::Node* node) {
1838 90 : const char* prefix = node->NamePrefix();
1839 5 : return prefix ? names->GetFormatted("%s %s", prefix, node->Name())
1840 95 : : names->GetCopy(node->Name());
1841 : }
1842 :
1843 : HeapEntry::Type EmbedderGraphNodeType(EmbedderGraphImpl::Node* node) {
1844 90 : return node->IsRootNode() ? HeapEntry::kSynthetic : HeapEntry::kNative;
1845 : }
1846 :
1847 : // Merges the names of an embedder node and its wrapper node.
1848 : // If the wrapper node name contains a tag suffix (part after '/') then the
1849 : // result is the embedder node name concatenated with the tag suffix.
1850 : // Otherwise, the result is the embedder node name.
1851 10 : const char* MergeNames(StringsStorage* names, const char* embedder_name,
1852 : const char* wrapper_name) {
1853 : const char* suffix = strchr(wrapper_name, '/');
1854 : return suffix ? names->GetFormatted("%s %s", embedder_name, suffix)
1855 10 : : embedder_name;
1856 : }
1857 :
1858 : } // anonymous namespace
1859 :
1860 80 : HeapEntry* EmbedderGraphEntriesAllocator::AllocateEntry(HeapThing ptr) {
1861 : EmbedderGraphImpl::Node* node =
1862 : reinterpret_cast<EmbedderGraphImpl::Node*>(ptr);
1863 : DCHECK(node->IsEmbedderNode());
1864 80 : size_t size = node->SizeInBytes();
1865 160 : return snapshot_->AddEntry(
1866 : EmbedderGraphNodeType(node), EmbedderGraphNodeName(names_, node),
1867 : static_cast<SnapshotObjectId>(reinterpret_cast<uintptr_t>(node) << 1),
1868 80 : static_cast<int>(size), 0);
1869 : }
1870 :
1871 398 : NativeObjectsExplorer::NativeObjectsExplorer(
1872 : HeapSnapshot* snapshot, SnapshottingProgressReportingInterface* progress)
1873 : : isolate_(
1874 : Isolate::FromHeap(snapshot->profiler()->heap_object_map()->heap())),
1875 : snapshot_(snapshot),
1876 : names_(snapshot_->profiler()->names()),
1877 : embedder_graph_entries_allocator_(
1878 1194 : new EmbedderGraphEntriesAllocator(snapshot)) {}
1879 :
1880 245 : HeapEntry* NativeObjectsExplorer::EntryForEmbedderGraphNode(
1881 : EmbedderGraphImpl::Node* node) {
1882 245 : EmbedderGraphImpl::Node* wrapper = node->WrapperNode();
1883 245 : if (wrapper) {
1884 : node = wrapper;
1885 : }
1886 245 : if (node->IsEmbedderNode()) {
1887 170 : return generator_->FindOrAddEntry(node,
1888 170 : embedder_graph_entries_allocator_.get());
1889 : } else {
1890 : EmbedderGraphImpl::V8NodeImpl* v8_node =
1891 : static_cast<EmbedderGraphImpl::V8NodeImpl*>(node);
1892 : Object object = v8_node->GetObject();
1893 75 : if (object->IsSmi()) return nullptr;
1894 75 : return generator_->FindEntry(
1895 : reinterpret_cast<void*>(Object::cast(object).ptr()));
1896 : }
1897 : }
1898 :
1899 393 : bool NativeObjectsExplorer::IterateAndExtractReferences(
1900 : HeapSnapshotGenerator* generator) {
1901 393 : generator_ = generator;
1902 :
1903 786 : if (FLAG_heap_profiler_use_embedder_graph &&
1904 393 : snapshot_->profiler()->HasBuildEmbedderGraphCallback()) {
1905 70 : v8::HandleScope scope(reinterpret_cast<v8::Isolate*>(isolate_));
1906 : DisallowHeapAllocation no_allocation;
1907 35 : EmbedderGraphImpl graph;
1908 35 : snapshot_->profiler()->BuildEmbedderGraph(isolate_, &graph);
1909 175 : for (const auto& node : graph.nodes()) {
1910 140 : if (node->IsRootNode()) {
1911 15 : snapshot_->root()->SetIndexedAutoIndexReference(
1912 15 : HeapGraphEdge::kElement, EntryForEmbedderGraphNode(node.get()));
1913 : }
1914 : // Adjust the name and the type of the V8 wrapper node.
1915 140 : auto wrapper = node->WrapperNode();
1916 140 : if (wrapper) {
1917 10 : HeapEntry* wrapper_entry = EntryForEmbedderGraphNode(wrapper);
1918 10 : wrapper_entry->set_name(
1919 : MergeNames(names_, EmbedderGraphNodeName(names_, node.get()),
1920 : wrapper_entry->name()));
1921 : wrapper_entry->set_type(EmbedderGraphNodeType(node.get()));
1922 : }
1923 : }
1924 : // Fill edges of the graph.
1925 145 : for (const auto& edge : graph.edges()) {
1926 110 : HeapEntry* from = EntryForEmbedderGraphNode(edge.from);
1927 : // |from| and |to| can be nullptr if the corresponding node is a V8 node
1928 : // pointing to a Smi.
1929 110 : if (!from) continue;
1930 110 : HeapEntry* to = EntryForEmbedderGraphNode(edge.to);
1931 110 : if (!to) continue;
1932 110 : if (edge.name == nullptr) {
1933 100 : from->SetIndexedAutoIndexReference(HeapGraphEdge::kElement, to);
1934 : } else {
1935 10 : from->SetNamedReference(HeapGraphEdge::kInternal,
1936 10 : names_->GetCopy(edge.name), to);
1937 : }
1938 : }
1939 : }
1940 393 : generator_ = nullptr;
1941 393 : return true;
1942 : }
1943 :
1944 398 : HeapSnapshotGenerator::HeapSnapshotGenerator(
1945 : HeapSnapshot* snapshot,
1946 : v8::ActivityControl* control,
1947 : v8::HeapProfiler::ObjectNameResolver* resolver,
1948 : Heap* heap)
1949 : : snapshot_(snapshot),
1950 : control_(control),
1951 : v8_heap_explorer_(snapshot_, this, resolver),
1952 : dom_explorer_(snapshot_, this),
1953 796 : heap_(heap) {
1954 398 : }
1955 :
1956 : namespace {
1957 : class NullContextScope {
1958 : public:
1959 : explicit NullContextScope(Isolate* isolate)
1960 : : isolate_(isolate), prev_(isolate->context()) {
1961 : isolate_->set_context(Context());
1962 : }
1963 : ~NullContextScope() { isolate_->set_context(prev_); }
1964 :
1965 : private:
1966 : Isolate* isolate_;
1967 : Context prev_;
1968 : };
1969 : } // namespace
1970 :
1971 398 : bool HeapSnapshotGenerator::GenerateSnapshot() {
1972 398 : v8_heap_explorer_.TagGlobalObjects();
1973 :
1974 : // TODO(1562) Profiler assumes that any object that is in the heap after
1975 : // full GC is reachable from the root when computing dominators.
1976 : // This is not true for weakly reachable objects.
1977 : // As a temporary solution we call GC twice.
1978 398 : heap_->PreciseCollectAllGarbage(Heap::kNoGCFlags,
1979 398 : GarbageCollectionReason::kHeapProfiler);
1980 398 : heap_->PreciseCollectAllGarbage(Heap::kNoGCFlags,
1981 398 : GarbageCollectionReason::kHeapProfiler);
1982 :
1983 398 : NullContextScope null_context_scope(Isolate::FromHeap(heap_));
1984 :
1985 : #ifdef VERIFY_HEAP
1986 : Heap* debug_heap = heap_;
1987 : if (FLAG_verify_heap) {
1988 : debug_heap->Verify();
1989 : }
1990 : #endif
1991 :
1992 : InitProgressCounter();
1993 :
1994 : #ifdef VERIFY_HEAP
1995 : if (FLAG_verify_heap) {
1996 : debug_heap->Verify();
1997 : }
1998 : #endif
1999 :
2000 398 : snapshot_->AddSyntheticRootEntries();
2001 :
2002 398 : if (!FillReferences()) return false;
2003 :
2004 393 : snapshot_->FillChildren();
2005 393 : snapshot_->RememberLastJSObjectId();
2006 :
2007 393 : progress_counter_ = progress_total_;
2008 393 : if (!ProgressReport(true)) return false;
2009 393 : return true;
2010 : }
2011 :
2012 3059591 : void HeapSnapshotGenerator::ProgressStep() {
2013 3059591 : ++progress_counter_;
2014 3059591 : }
2015 :
2016 3027638 : bool HeapSnapshotGenerator::ProgressReport(bool force) {
2017 : const int kProgressReportGranularity = 10000;
2018 3027638 : if (control_ != nullptr &&
2019 65493 : (force || progress_counter_ % kProgressReportGranularity == 0)) {
2020 35 : return control_->ReportProgressValue(progress_counter_, progress_total_) ==
2021 35 : v8::ActivityControl::kContinue;
2022 : }
2023 : return true;
2024 : }
2025 :
2026 0 : void HeapSnapshotGenerator::InitProgressCounter() {
2027 398 : if (control_ == nullptr) return;
2028 : // The +1 ensures that intermediate ProgressReport calls will never signal
2029 : // that the work is finished (i.e. progress_counter_ == progress_total_).
2030 : // Only the forced ProgressReport() at the end of GenerateSnapshot()
2031 : // should signal that the work is finished because signalling finished twice
2032 : // breaks the DevTools frontend.
2033 15 : progress_total_ = v8_heap_explorer_.EstimateObjectsCount() + 1;
2034 15 : progress_counter_ = 0;
2035 : }
2036 :
2037 398 : bool HeapSnapshotGenerator::FillReferences() {
2038 791 : return v8_heap_explorer_.IterateAndExtractReferences(this) &&
2039 791 : dom_explorer_.IterateAndExtractReferences(this);
2040 : }
2041 :
2042 : template<int bytes> struct MaxDecimalDigitsIn;
2043 : template<> struct MaxDecimalDigitsIn<4> {
2044 : static const int kSigned = 11;
2045 : static const int kUnsigned = 10;
2046 : };
2047 : template<> struct MaxDecimalDigitsIn<8> {
2048 : static const int kSigned = 20;
2049 : static const int kUnsigned = 20;
2050 : };
2051 :
2052 35 : class OutputStreamWriter {
2053 : public:
2054 35 : explicit OutputStreamWriter(v8::OutputStream* stream)
2055 : : stream_(stream),
2056 35 : chunk_size_(stream->GetChunkSize()),
2057 : chunk_(chunk_size_),
2058 : chunk_pos_(0),
2059 105 : aborted_(false) {
2060 : DCHECK_GT(chunk_size_, 0);
2061 35 : }
2062 : bool aborted() { return aborted_; }
2063 : void AddCharacter(char c) {
2064 : DCHECK_NE(c, '\0');
2065 : DCHECK(chunk_pos_ < chunk_size_);
2066 9625060 : chunk_[chunk_pos_++] = c;
2067 4812530 : MaybeWriteChunk();
2068 : }
2069 : void AddString(const char* s) {
2070 968995 : AddSubstring(s, StrLength(s));
2071 : }
2072 968995 : void AddSubstring(const char* s, int n) {
2073 968995 : if (n <= 0) return;
2074 : DCHECK(static_cast<size_t>(n) <= strlen(s));
2075 968995 : const char* s_end = s + n;
2076 2911235 : while (s < s_end) {
2077 : int s_chunk_size =
2078 971120 : Min(chunk_size_ - chunk_pos_, static_cast<int>(s_end - s));
2079 : DCHECK_GT(s_chunk_size, 0);
2080 971120 : MemCopy(chunk_.start() + chunk_pos_, s, s_chunk_size);
2081 971120 : s += s_chunk_size;
2082 971120 : chunk_pos_ += s_chunk_size;
2083 971120 : MaybeWriteChunk();
2084 : }
2085 : }
2086 105 : void AddNumber(unsigned n) { AddNumberImpl<unsigned>(n, "%u"); }
2087 30 : void Finalize() {
2088 30 : if (aborted_) return;
2089 : DCHECK(chunk_pos_ < chunk_size_);
2090 30 : if (chunk_pos_ != 0) {
2091 : WriteChunk();
2092 : }
2093 30 : stream_->EndOfStream();
2094 : }
2095 :
2096 : private:
2097 : template<typename T>
2098 105 : void AddNumberImpl(T n, const char* format) {
2099 : // Buffer for the longest value plus trailing \0
2100 : static const int kMaxNumberSize =
2101 : MaxDecimalDigitsIn<sizeof(T)>::kUnsigned + 1;
2102 105 : if (chunk_size_ - chunk_pos_ >= kMaxNumberSize) {
2103 105 : int result = SNPrintF(
2104 105 : chunk_.SubVector(chunk_pos_, chunk_size_), format, n);
2105 : DCHECK_NE(result, -1);
2106 105 : chunk_pos_ += result;
2107 105 : MaybeWriteChunk();
2108 : } else {
2109 : EmbeddedVector<char, kMaxNumberSize> buffer;
2110 0 : int result = SNPrintF(buffer, format, n);
2111 : USE(result);
2112 : DCHECK_NE(result, -1);
2113 : AddString(buffer.start());
2114 : }
2115 105 : }
2116 5783755 : void MaybeWriteChunk() {
2117 : DCHECK(chunk_pos_ <= chunk_size_);
2118 5783755 : if (chunk_pos_ == chunk_size_) {
2119 : WriteChunk();
2120 : }
2121 5783755 : }
2122 : void WriteChunk() {
2123 3144 : if (aborted_) return;
2124 3144 : if (stream_->WriteAsciiChunk(chunk_.start(), chunk_pos_) ==
2125 5 : v8::OutputStream::kAbort) aborted_ = true;
2126 3144 : chunk_pos_ = 0;
2127 : }
2128 :
2129 : v8::OutputStream* stream_;
2130 : int chunk_size_;
2131 : ScopedVector<char> chunk_;
2132 : int chunk_pos_;
2133 : bool aborted_;
2134 : };
2135 :
2136 :
2137 : // type, name|index, to_node.
2138 : const int HeapSnapshotJSONSerializer::kEdgeFieldsCount = 3;
2139 : // type, name, id, self_size, edge_count, trace_node_id.
2140 : const int HeapSnapshotJSONSerializer::kNodeFieldsCount = 6;
2141 :
2142 35 : void HeapSnapshotJSONSerializer::Serialize(v8::OutputStream* stream) {
2143 35 : if (AllocationTracker* allocation_tracker =
2144 35 : snapshot_->profiler()->allocation_tracker()) {
2145 0 : allocation_tracker->PrepareForSerialization();
2146 : }
2147 : DCHECK_NULL(writer_);
2148 35 : writer_ = new OutputStreamWriter(stream);
2149 35 : SerializeImpl();
2150 70 : delete writer_;
2151 35 : writer_ = nullptr;
2152 35 : }
2153 :
2154 :
2155 35 : void HeapSnapshotJSONSerializer::SerializeImpl() {
2156 : DCHECK_EQ(0, snapshot_->root()->index());
2157 35 : writer_->AddCharacter('{');
2158 35 : writer_->AddString("\"snapshot\":{");
2159 35 : SerializeSnapshot();
2160 35 : if (writer_->aborted()) return;
2161 : writer_->AddString("},\n");
2162 35 : writer_->AddString("\"nodes\":[");
2163 35 : SerializeNodes();
2164 35 : if (writer_->aborted()) return;
2165 : writer_->AddString("],\n");
2166 30 : writer_->AddString("\"edges\":[");
2167 30 : SerializeEdges();
2168 30 : if (writer_->aborted()) return;
2169 : writer_->AddString("],\n");
2170 :
2171 30 : writer_->AddString("\"trace_function_infos\":[");
2172 30 : SerializeTraceNodeInfos();
2173 30 : if (writer_->aborted()) return;
2174 : writer_->AddString("],\n");
2175 30 : writer_->AddString("\"trace_tree\":[");
2176 : SerializeTraceTree();
2177 30 : if (writer_->aborted()) return;
2178 : writer_->AddString("],\n");
2179 :
2180 30 : writer_->AddString("\"samples\":[");
2181 30 : SerializeSamples();
2182 30 : if (writer_->aborted()) return;
2183 : writer_->AddString("],\n");
2184 :
2185 30 : writer_->AddString("\"locations\":[");
2186 30 : SerializeLocations();
2187 30 : if (writer_->aborted()) return;
2188 : writer_->AddString("],\n");
2189 :
2190 30 : writer_->AddString("\"strings\":[");
2191 30 : SerializeStrings();
2192 30 : if (writer_->aborted()) return;
2193 : writer_->AddCharacter(']');
2194 30 : writer_->AddCharacter('}');
2195 30 : writer_->Finalize();
2196 : }
2197 :
2198 :
2199 887592 : int HeapSnapshotJSONSerializer::GetStringId(const char* s) {
2200 : base::HashMap::Entry* cache_entry =
2201 1775184 : strings_.LookupOrInsert(const_cast<char*>(s), StringHash(s));
2202 887592 : if (cache_entry->value == nullptr) {
2203 201262 : cache_entry->value = reinterpret_cast<void*>(next_string_id_++);
2204 : }
2205 887592 : return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
2206 : }
2207 :
2208 :
2209 : namespace {
2210 :
2211 : template<size_t size> struct ToUnsigned;
2212 :
2213 : template<> struct ToUnsigned<4> {
2214 : typedef uint32_t Type;
2215 : };
2216 :
2217 : template<> struct ToUnsigned<8> {
2218 : typedef uint64_t Type;
2219 : };
2220 :
2221 : } // namespace
2222 :
2223 :
2224 : template<typename T>
2225 : static int utoa_impl(T value, const Vector<char>& buffer, int buffer_pos) {
2226 : STATIC_ASSERT(static_cast<T>(-1) > 0); // Check that T is unsigned
2227 : int number_of_digits = 0;
2228 : T t = value;
2229 9251958 : do {
2230 9251958 : ++number_of_digits;
2231 : } while (t /= 10);
2232 :
2233 3535614 : buffer_pos += number_of_digits;
2234 : int result = buffer_pos;
2235 9251958 : do {
2236 9251958 : int last_digit = static_cast<int>(value % 10);
2237 18503916 : buffer[--buffer_pos] = '0' + last_digit;
2238 9251958 : value /= 10;
2239 : } while (value);
2240 : return result;
2241 : }
2242 :
2243 :
2244 : template<typename T>
2245 : static int utoa(T value, const Vector<char>& buffer, int buffer_pos) {
2246 1935230 : typename ToUnsigned<sizeof(value)>::Type unsigned_value = value;
2247 : STATIC_ASSERT(sizeof(value) == sizeof(unsigned_value));
2248 : return utoa_impl(unsigned_value, buffer, buffer_pos);
2249 : }
2250 :
2251 :
2252 755597 : void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge,
2253 : bool first_edge) {
2254 : // The buffer needs space for 3 unsigned ints, 3 commas, \n and \0
2255 : static const int kBufferSize =
2256 : MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned * 3 + 3 + 2; // NOLINT
2257 : EmbeddedVector<char, kBufferSize> buffer;
2258 : int edge_name_or_index = edge->type() == HeapGraphEdge::kElement
2259 754839 : || edge->type() == HeapGraphEdge::kHidden
2260 834890 : ? edge->index() : GetStringId(edge->name());
2261 : int buffer_pos = 0;
2262 755597 : if (!first_edge) {
2263 755567 : buffer[buffer_pos++] = ',';
2264 : }
2265 : buffer_pos = utoa(edge->type(), buffer, buffer_pos);
2266 1511194 : buffer[buffer_pos++] = ',';
2267 : buffer_pos = utoa(edge_name_or_index, buffer, buffer_pos);
2268 1511194 : buffer[buffer_pos++] = ',';
2269 : buffer_pos = utoa(to_node_index(edge->to()), buffer, buffer_pos);
2270 1511194 : buffer[buffer_pos++] = '\n';
2271 1511194 : buffer[buffer_pos++] = '\0';
2272 755597 : writer_->AddString(buffer.start());
2273 755597 : }
2274 :
2275 30 : void HeapSnapshotJSONSerializer::SerializeEdges() {
2276 30 : std::vector<HeapGraphEdge*>& edges = snapshot_->children();
2277 1511224 : for (size_t i = 0; i < edges.size(); ++i) {
2278 : DCHECK(i == 0 ||
2279 : edges[i - 1]->from()->index() <= edges[i]->from()->index());
2280 1511194 : SerializeEdge(edges[i], i == 0);
2281 755597 : if (writer_->aborted()) return;
2282 : }
2283 : }
2284 :
2285 211288 : void HeapSnapshotJSONSerializer::SerializeNode(const HeapEntry* entry) {
2286 : // The buffer needs space for 4 unsigned ints, 1 size_t, 5 commas, \n and \0
2287 : static const int kBufferSize =
2288 : 5 * MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned // NOLINT
2289 : + MaxDecimalDigitsIn<sizeof(size_t)>::kUnsigned // NOLINT
2290 : + 6 + 1 + 1;
2291 : EmbeddedVector<char, kBufferSize> buffer;
2292 : int buffer_pos = 0;
2293 211288 : if (to_node_index(entry) != 0) {
2294 211253 : buffer[buffer_pos++] = ',';
2295 : }
2296 : buffer_pos = utoa(entry->type(), buffer, buffer_pos);
2297 422576 : buffer[buffer_pos++] = ',';
2298 211288 : buffer_pos = utoa(GetStringId(entry->name()), buffer, buffer_pos);
2299 422576 : buffer[buffer_pos++] = ',';
2300 : buffer_pos = utoa(entry->id(), buffer, buffer_pos);
2301 422576 : buffer[buffer_pos++] = ',';
2302 : buffer_pos = utoa(entry->self_size(), buffer, buffer_pos);
2303 422576 : buffer[buffer_pos++] = ',';
2304 : buffer_pos = utoa(entry->children_count(), buffer, buffer_pos);
2305 422576 : buffer[buffer_pos++] = ',';
2306 : buffer_pos = utoa(entry->trace_node_id(), buffer, buffer_pos);
2307 422576 : buffer[buffer_pos++] = '\n';
2308 422576 : buffer[buffer_pos++] = '\0';
2309 211288 : writer_->AddString(buffer.start());
2310 211288 : }
2311 :
2312 35 : void HeapSnapshotJSONSerializer::SerializeNodes() {
2313 35 : const std::deque<HeapEntry>& entries = snapshot_->entries();
2314 211318 : for (const HeapEntry& entry : entries) {
2315 211288 : SerializeNode(&entry);
2316 211288 : if (writer_->aborted()) return;
2317 : }
2318 : }
2319 :
2320 35 : void HeapSnapshotJSONSerializer::SerializeSnapshot() {
2321 35 : writer_->AddString("\"meta\":");
2322 : // The object describing node serialization layout.
2323 : // We use a set of macros to improve readability.
2324 :
2325 : // clang-format off
2326 : #define JSON_A(s) "[" s "]"
2327 : #define JSON_O(s) "{" s "}"
2328 : #define JSON_S(s) "\"" s "\""
2329 35 : writer_->AddString(JSON_O(
2330 : JSON_S("node_fields") ":" JSON_A(
2331 : JSON_S("type") ","
2332 : JSON_S("name") ","
2333 : JSON_S("id") ","
2334 : JSON_S("self_size") ","
2335 : JSON_S("edge_count") ","
2336 : JSON_S("trace_node_id")) ","
2337 : JSON_S("node_types") ":" JSON_A(
2338 : JSON_A(
2339 : JSON_S("hidden") ","
2340 : JSON_S("array") ","
2341 : JSON_S("string") ","
2342 : JSON_S("object") ","
2343 : JSON_S("code") ","
2344 : JSON_S("closure") ","
2345 : JSON_S("regexp") ","
2346 : JSON_S("number") ","
2347 : JSON_S("native") ","
2348 : JSON_S("synthetic") ","
2349 : JSON_S("concatenated string") ","
2350 : JSON_S("sliced string") ","
2351 : JSON_S("symbol") ","
2352 : JSON_S("bigint")) ","
2353 : JSON_S("string") ","
2354 : JSON_S("number") ","
2355 : JSON_S("number") ","
2356 : JSON_S("number") ","
2357 : JSON_S("number") ","
2358 : JSON_S("number")) ","
2359 : JSON_S("edge_fields") ":" JSON_A(
2360 : JSON_S("type") ","
2361 : JSON_S("name_or_index") ","
2362 : JSON_S("to_node")) ","
2363 : JSON_S("edge_types") ":" JSON_A(
2364 : JSON_A(
2365 : JSON_S("context") ","
2366 : JSON_S("element") ","
2367 : JSON_S("property") ","
2368 : JSON_S("internal") ","
2369 : JSON_S("hidden") ","
2370 : JSON_S("shortcut") ","
2371 : JSON_S("weak")) ","
2372 : JSON_S("string_or_number") ","
2373 : JSON_S("node")) ","
2374 : JSON_S("trace_function_info_fields") ":" JSON_A(
2375 : JSON_S("function_id") ","
2376 : JSON_S("name") ","
2377 : JSON_S("script_name") ","
2378 : JSON_S("script_id") ","
2379 : JSON_S("line") ","
2380 : JSON_S("column")) ","
2381 : JSON_S("trace_node_fields") ":" JSON_A(
2382 : JSON_S("id") ","
2383 : JSON_S("function_info_index") ","
2384 : JSON_S("count") ","
2385 : JSON_S("size") ","
2386 : JSON_S("children")) ","
2387 : JSON_S("sample_fields") ":" JSON_A(
2388 : JSON_S("timestamp_us") ","
2389 : JSON_S("last_assigned_id")) ","
2390 : JSON_S("location_fields") ":" JSON_A(
2391 : JSON_S("object_index") ","
2392 : JSON_S("script_id") ","
2393 : JSON_S("line") ","
2394 : JSON_S("column"))));
2395 : // clang-format on
2396 : #undef JSON_S
2397 : #undef JSON_O
2398 : #undef JSON_A
2399 35 : writer_->AddString(",\"node_count\":");
2400 70 : writer_->AddNumber(static_cast<unsigned>(snapshot_->entries().size()));
2401 35 : writer_->AddString(",\"edge_count\":");
2402 70 : writer_->AddNumber(static_cast<double>(snapshot_->edges().size()));
2403 35 : writer_->AddString(",\"trace_function_count\":");
2404 : uint32_t count = 0;
2405 35 : AllocationTracker* tracker = snapshot_->profiler()->allocation_tracker();
2406 35 : if (tracker) {
2407 0 : count = static_cast<uint32_t>(tracker->function_info_list().size());
2408 : }
2409 35 : writer_->AddNumber(count);
2410 35 : }
2411 :
2412 :
2413 20 : static void WriteUChar(OutputStreamWriter* w, unibrow::uchar u) {
2414 : static const char hex_chars[] = "0123456789ABCDEF";
2415 : w->AddString("\\u");
2416 20 : w->AddCharacter(hex_chars[(u >> 12) & 0xF]);
2417 20 : w->AddCharacter(hex_chars[(u >> 8) & 0xF]);
2418 20 : w->AddCharacter(hex_chars[(u >> 4) & 0xF]);
2419 20 : w->AddCharacter(hex_chars[u & 0xF]);
2420 20 : }
2421 :
2422 :
2423 0 : void HeapSnapshotJSONSerializer::SerializeTraceTree() {
2424 30 : AllocationTracker* tracker = snapshot_->profiler()->allocation_tracker();
2425 30 : if (!tracker) return;
2426 : AllocationTraceTree* traces = tracker->trace_tree();
2427 0 : SerializeTraceNode(traces->root());
2428 : }
2429 :
2430 :
2431 0 : void HeapSnapshotJSONSerializer::SerializeTraceNode(AllocationTraceNode* node) {
2432 : // The buffer needs space for 4 unsigned ints, 4 commas, [ and \0
2433 : const int kBufferSize =
2434 : 4 * MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned // NOLINT
2435 : + 4 + 1 + 1;
2436 : EmbeddedVector<char, kBufferSize> buffer;
2437 : int buffer_pos = 0;
2438 : buffer_pos = utoa(node->id(), buffer, buffer_pos);
2439 0 : buffer[buffer_pos++] = ',';
2440 : buffer_pos = utoa(node->function_info_index(), buffer, buffer_pos);
2441 0 : buffer[buffer_pos++] = ',';
2442 : buffer_pos = utoa(node->allocation_count(), buffer, buffer_pos);
2443 0 : buffer[buffer_pos++] = ',';
2444 : buffer_pos = utoa(node->allocation_size(), buffer, buffer_pos);
2445 0 : buffer[buffer_pos++] = ',';
2446 0 : buffer[buffer_pos++] = '[';
2447 0 : buffer[buffer_pos++] = '\0';
2448 0 : writer_->AddString(buffer.start());
2449 :
2450 : int i = 0;
2451 0 : for (AllocationTraceNode* child : node->children()) {
2452 0 : if (i++ > 0) {
2453 0 : writer_->AddCharacter(',');
2454 : }
2455 0 : SerializeTraceNode(child);
2456 : }
2457 0 : writer_->AddCharacter(']');
2458 0 : }
2459 :
2460 :
2461 : // 0-based position is converted to 1-based during the serialization.
2462 0 : static int SerializePosition(int position, const Vector<char>& buffer,
2463 : int buffer_pos) {
2464 0 : if (position == -1) {
2465 0 : buffer[buffer_pos++] = '0';
2466 : } else {
2467 : DCHECK_GE(position, 0);
2468 0 : buffer_pos = utoa(static_cast<unsigned>(position + 1), buffer, buffer_pos);
2469 : }
2470 0 : return buffer_pos;
2471 : }
2472 :
2473 :
2474 30 : void HeapSnapshotJSONSerializer::SerializeTraceNodeInfos() {
2475 30 : AllocationTracker* tracker = snapshot_->profiler()->allocation_tracker();
2476 60 : if (!tracker) return;
2477 : // The buffer needs space for 6 unsigned ints, 6 commas, \n and \0
2478 : const int kBufferSize =
2479 : 6 * MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned // NOLINT
2480 : + 6 + 1 + 1;
2481 : EmbeddedVector<char, kBufferSize> buffer;
2482 : int i = 0;
2483 0 : for (AllocationTracker::FunctionInfo* info : tracker->function_info_list()) {
2484 : int buffer_pos = 0;
2485 0 : if (i++ > 0) {
2486 0 : buffer[buffer_pos++] = ',';
2487 : }
2488 0 : buffer_pos = utoa(info->function_id, buffer, buffer_pos);
2489 0 : buffer[buffer_pos++] = ',';
2490 0 : buffer_pos = utoa(GetStringId(info->name), buffer, buffer_pos);
2491 0 : buffer[buffer_pos++] = ',';
2492 0 : buffer_pos = utoa(GetStringId(info->script_name), buffer, buffer_pos);
2493 0 : buffer[buffer_pos++] = ',';
2494 : // The cast is safe because script id is a non-negative Smi.
2495 0 : buffer_pos = utoa(static_cast<unsigned>(info->script_id), buffer,
2496 : buffer_pos);
2497 0 : buffer[buffer_pos++] = ',';
2498 0 : buffer_pos = SerializePosition(info->line, buffer, buffer_pos);
2499 0 : buffer[buffer_pos++] = ',';
2500 0 : buffer_pos = SerializePosition(info->column, buffer, buffer_pos);
2501 0 : buffer[buffer_pos++] = '\n';
2502 0 : buffer[buffer_pos++] = '\0';
2503 0 : writer_->AddString(buffer.start());
2504 : }
2505 : }
2506 :
2507 :
2508 30 : void HeapSnapshotJSONSerializer::SerializeSamples() {
2509 : const std::vector<HeapObjectsMap::TimeInterval>& samples =
2510 30 : snapshot_->profiler()->heap_object_map()->samples();
2511 60 : if (samples.empty()) return;
2512 0 : base::TimeTicks start_time = samples[0].timestamp;
2513 : // The buffer needs space for 2 unsigned ints, 2 commas, \n and \0
2514 : const int kBufferSize = MaxDecimalDigitsIn<sizeof(
2515 : base::TimeDelta().InMicroseconds())>::kUnsigned +
2516 : MaxDecimalDigitsIn<sizeof(samples[0].id)>::kUnsigned +
2517 : 2 + 1 + 1;
2518 : EmbeddedVector<char, kBufferSize> buffer;
2519 : int i = 0;
2520 0 : for (const HeapObjectsMap::TimeInterval& sample : samples) {
2521 : int buffer_pos = 0;
2522 0 : if (i++ > 0) {
2523 0 : buffer[buffer_pos++] = ',';
2524 : }
2525 0 : base::TimeDelta time_delta = sample.timestamp - start_time;
2526 0 : buffer_pos = utoa(time_delta.InMicroseconds(), buffer, buffer_pos);
2527 0 : buffer[buffer_pos++] = ',';
2528 : buffer_pos = utoa(sample.last_assigned_id(), buffer, buffer_pos);
2529 0 : buffer[buffer_pos++] = '\n';
2530 0 : buffer[buffer_pos++] = '\0';
2531 0 : writer_->AddString(buffer.start());
2532 : }
2533 : }
2534 :
2535 :
2536 200632 : void HeapSnapshotJSONSerializer::SerializeString(const unsigned char* s) {
2537 200632 : writer_->AddCharacter('\n');
2538 200632 : writer_->AddCharacter('\"');
2539 8221076 : for ( ; *s != '\0'; ++s) {
2540 4010222 : switch (*s) {
2541 : case '\b':
2542 5 : writer_->AddString("\\b");
2543 : continue;
2544 : case '\f':
2545 0 : writer_->AddString("\\f");
2546 : continue;
2547 : case '\n':
2548 1045 : writer_->AddString("\\n");
2549 : continue;
2550 : case '\r':
2551 5 : writer_->AddString("\\r");
2552 : continue;
2553 : case '\t':
2554 0 : writer_->AddString("\\t");
2555 : continue;
2556 : case '\"':
2557 : case '\\':
2558 345 : writer_->AddCharacter('\\');
2559 345 : writer_->AddCharacter(*s);
2560 : continue;
2561 : default:
2562 4008822 : if (*s > 31 && *s < 128) {
2563 4008802 : writer_->AddCharacter(*s);
2564 20 : } else if (*s <= 31) {
2565 : // Special character with no dedicated literal.
2566 0 : WriteUChar(writer_, *s);
2567 : } else {
2568 : // Convert UTF-8 into \u UTF-16 literal.
2569 20 : size_t length = 1, cursor = 0;
2570 90 : for ( ; length <= 4 && *(s + length) != '\0'; ++length) { }
2571 20 : unibrow::uchar c = unibrow::Utf8::CalculateValue(s, length, &cursor);
2572 20 : if (c != unibrow::Utf8::kBadChar) {
2573 20 : WriteUChar(writer_, c);
2574 : DCHECK_NE(cursor, 0);
2575 20 : s += cursor - 1;
2576 : } else {
2577 0 : writer_->AddCharacter('?');
2578 : }
2579 : }
2580 : }
2581 : }
2582 200632 : writer_->AddCharacter('\"');
2583 200632 : }
2584 :
2585 :
2586 30 : void HeapSnapshotJSONSerializer::SerializeStrings() {
2587 : ScopedVector<const unsigned char*> sorted_strings(
2588 30 : strings_.occupancy() + 1);
2589 401294 : for (base::HashMap::Entry* entry = strings_.Start(); entry != nullptr;
2590 : entry = strings_.Next(entry)) {
2591 200632 : int index = static_cast<int>(reinterpret_cast<uintptr_t>(entry->value));
2592 401264 : sorted_strings[index] = reinterpret_cast<const unsigned char*>(entry->key);
2593 : }
2594 30 : writer_->AddString("\"<dummy>\"");
2595 401294 : for (int i = 1; i < sorted_strings.length(); ++i) {
2596 200632 : writer_->AddCharacter(',');
2597 401264 : SerializeString(sorted_strings[i]);
2598 200632 : if (writer_->aborted()) return;
2599 : }
2600 : }
2601 :
2602 365 : void HeapSnapshotJSONSerializer::SerializeLocation(
2603 : const SourceLocation& location) {
2604 : // The buffer needs space for 4 unsigned ints, 3 commas, \n and \0
2605 : static const int kBufferSize =
2606 : MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned * 4 + 3 + 2;
2607 : EmbeddedVector<char, kBufferSize> buffer;
2608 : int buffer_pos = 0;
2609 365 : buffer_pos = utoa(to_node_index(location.entry_index), buffer, buffer_pos);
2610 730 : buffer[buffer_pos++] = ',';
2611 365 : buffer_pos = utoa(location.scriptId, buffer, buffer_pos);
2612 730 : buffer[buffer_pos++] = ',';
2613 365 : buffer_pos = utoa(location.line, buffer, buffer_pos);
2614 730 : buffer[buffer_pos++] = ',';
2615 365 : buffer_pos = utoa(location.col, buffer, buffer_pos);
2616 730 : buffer[buffer_pos++] = '\n';
2617 730 : buffer[buffer_pos++] = '\0';
2618 365 : writer_->AddString(buffer.start());
2619 365 : }
2620 :
2621 30 : void HeapSnapshotJSONSerializer::SerializeLocations() {
2622 30 : const std::vector<SourceLocation>& locations = snapshot_->locations();
2623 760 : for (size_t i = 0; i < locations.size(); i++) {
2624 365 : if (i > 0) writer_->AddCharacter(',');
2625 365 : SerializeLocation(locations[i]);
2626 365 : if (writer_->aborted()) return;
2627 : }
2628 : }
2629 :
2630 : } // namespace internal
2631 122036 : } // namespace v8
|