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