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