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