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