blob: 217d1ca25ecfa9dae22b7270b973700c67a2a09f [file] [log] [blame]
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001// Copyright 2013 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "v8.h"
29
30#include "heap-snapshot-generator-inl.h"
31
32#include "heap-profiler.h"
33#include "debug.h"
danno@chromium.org41728482013-06-12 22:31:22 +000034#include "types.h"
ulan@chromium.org2e04b582013-02-21 14:06:02 +000035
36namespace v8 {
37namespace internal {
38
39
40HeapGraphEdge::HeapGraphEdge(Type type, const char* name, int from, int to)
41 : type_(type),
42 from_index_(from),
43 to_index_(to),
44 name_(name) {
45 ASSERT(type == kContextVariable
46 || type == kProperty
47 || type == kInternal
48 || type == kShortcut);
49}
50
51
52HeapGraphEdge::HeapGraphEdge(Type type, int index, int from, int to)
53 : type_(type),
54 from_index_(from),
55 to_index_(to),
56 index_(index) {
57 ASSERT(type == kElement || type == kHidden || type == kWeak);
58}
59
60
61void HeapGraphEdge::ReplaceToIndexWithEntry(HeapSnapshot* snapshot) {
62 to_entry_ = &snapshot->entries()[to_index_];
63}
64
65
66const int HeapEntry::kNoEntry = -1;
67
68HeapEntry::HeapEntry(HeapSnapshot* snapshot,
69 Type type,
70 const char* name,
71 SnapshotObjectId id,
72 int self_size)
73 : type_(type),
74 children_count_(0),
75 children_index_(-1),
76 self_size_(self_size),
77 id_(id),
78 snapshot_(snapshot),
79 name_(name) { }
80
81
82void HeapEntry::SetNamedReference(HeapGraphEdge::Type type,
83 const char* name,
84 HeapEntry* entry) {
85 HeapGraphEdge edge(type, name, this->index(), entry->index());
86 snapshot_->edges().Add(edge);
87 ++children_count_;
88}
89
90
91void HeapEntry::SetIndexedReference(HeapGraphEdge::Type type,
92 int index,
93 HeapEntry* entry) {
94 HeapGraphEdge edge(type, index, this->index(), entry->index());
95 snapshot_->edges().Add(edge);
96 ++children_count_;
97}
98
99
100Handle<HeapObject> HeapEntry::GetHeapObject() {
101 return snapshot_->collection()->FindHeapObjectById(id());
102}
103
104
105void HeapEntry::Print(
106 const char* prefix, const char* edge_name, int max_depth, int indent) {
107 STATIC_CHECK(sizeof(unsigned) == sizeof(id()));
108 OS::Print("%6d @%6u %*c %s%s: ",
109 self_size(), id(), indent, ' ', prefix, edge_name);
110 if (type() != kString) {
111 OS::Print("%s %.40s\n", TypeAsString(), name_);
112 } else {
113 OS::Print("\"");
114 const char* c = name_;
115 while (*c && (c - name_) <= 40) {
116 if (*c != '\n')
117 OS::Print("%c", *c);
118 else
119 OS::Print("\\n");
120 ++c;
121 }
122 OS::Print("\"\n");
123 }
124 if (--max_depth == 0) return;
125 Vector<HeapGraphEdge*> ch = children();
126 for (int i = 0; i < ch.length(); ++i) {
127 HeapGraphEdge& edge = *ch[i];
128 const char* edge_prefix = "";
129 EmbeddedVector<char, 64> index;
130 const char* edge_name = index.start();
131 switch (edge.type()) {
132 case HeapGraphEdge::kContextVariable:
133 edge_prefix = "#";
134 edge_name = edge.name();
135 break;
136 case HeapGraphEdge::kElement:
137 OS::SNPrintF(index, "%d", edge.index());
138 break;
139 case HeapGraphEdge::kInternal:
140 edge_prefix = "$";
141 edge_name = edge.name();
142 break;
143 case HeapGraphEdge::kProperty:
144 edge_name = edge.name();
145 break;
146 case HeapGraphEdge::kHidden:
147 edge_prefix = "$";
148 OS::SNPrintF(index, "%d", edge.index());
149 break;
150 case HeapGraphEdge::kShortcut:
151 edge_prefix = "^";
152 edge_name = edge.name();
153 break;
154 case HeapGraphEdge::kWeak:
155 edge_prefix = "w";
156 OS::SNPrintF(index, "%d", edge.index());
157 break;
158 default:
159 OS::SNPrintF(index, "!!! unknown edge type: %d ", edge.type());
160 }
161 edge.to()->Print(edge_prefix, edge_name, max_depth, indent + 2);
162 }
163}
164
165
166const char* HeapEntry::TypeAsString() {
167 switch (type()) {
168 case kHidden: return "/hidden/";
169 case kObject: return "/object/";
170 case kClosure: return "/closure/";
171 case kString: return "/string/";
172 case kCode: return "/code/";
173 case kArray: return "/array/";
174 case kRegExp: return "/regexp/";
175 case kHeapNumber: return "/number/";
176 case kNative: return "/native/";
177 case kSynthetic: return "/synthetic/";
178 default: return "???";
179 }
180}
181
182
183// It is very important to keep objects that form a heap snapshot
184// as small as possible.
185namespace { // Avoid littering the global namespace.
186
187template <size_t ptr_size> struct SnapshotSizeConstants;
188
189template <> struct SnapshotSizeConstants<4> {
190 static const int kExpectedHeapGraphEdgeSize = 12;
191 static const int kExpectedHeapEntrySize = 24;
192 static const int kExpectedHeapSnapshotsCollectionSize = 100;
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +0000193 static const int kExpectedHeapSnapshotSize = 132;
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000194};
195
196template <> struct SnapshotSizeConstants<8> {
197 static const int kExpectedHeapGraphEdgeSize = 24;
198 static const int kExpectedHeapEntrySize = 32;
199 static const int kExpectedHeapSnapshotsCollectionSize = 152;
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +0000200 static const int kExpectedHeapSnapshotSize = 160;
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000201};
202
203} // namespace
204
205HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000206 const char* title,
207 unsigned uid)
208 : collection_(collection),
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000209 title_(title),
210 uid_(uid),
211 root_index_(HeapEntry::kNoEntry),
212 gc_roots_index_(HeapEntry::kNoEntry),
213 natives_root_index_(HeapEntry::kNoEntry),
214 max_snapshot_js_object_id_(0) {
215 STATIC_CHECK(
216 sizeof(HeapGraphEdge) ==
217 SnapshotSizeConstants<kPointerSize>::kExpectedHeapGraphEdgeSize);
218 STATIC_CHECK(
219 sizeof(HeapEntry) ==
220 SnapshotSizeConstants<kPointerSize>::kExpectedHeapEntrySize);
221 for (int i = 0; i < VisitorSynchronization::kNumberOfSyncTags; ++i) {
222 gc_subroot_indexes_[i] = HeapEntry::kNoEntry;
223 }
224}
225
226
227void HeapSnapshot::Delete() {
228 collection_->RemoveSnapshot(this);
229 delete this;
230}
231
232
233void HeapSnapshot::RememberLastJSObjectId() {
234 max_snapshot_js_object_id_ = collection_->last_assigned_id();
235}
236
237
238HeapEntry* HeapSnapshot::AddRootEntry() {
239 ASSERT(root_index_ == HeapEntry::kNoEntry);
240 ASSERT(entries_.is_empty()); // Root entry must be the first one.
241 HeapEntry* entry = AddEntry(HeapEntry::kObject,
242 "",
243 HeapObjectsMap::kInternalRootObjectId,
244 0);
245 root_index_ = entry->index();
246 ASSERT(root_index_ == 0);
247 return entry;
248}
249
250
251HeapEntry* HeapSnapshot::AddGcRootsEntry() {
252 ASSERT(gc_roots_index_ == HeapEntry::kNoEntry);
253 HeapEntry* entry = AddEntry(HeapEntry::kObject,
254 "(GC roots)",
255 HeapObjectsMap::kGcRootsObjectId,
256 0);
257 gc_roots_index_ = entry->index();
258 return entry;
259}
260
261
262HeapEntry* HeapSnapshot::AddGcSubrootEntry(int tag) {
263 ASSERT(gc_subroot_indexes_[tag] == HeapEntry::kNoEntry);
264 ASSERT(0 <= tag && tag < VisitorSynchronization::kNumberOfSyncTags);
265 HeapEntry* entry = AddEntry(
266 HeapEntry::kObject,
267 VisitorSynchronization::kTagNames[tag],
268 HeapObjectsMap::GetNthGcSubrootId(tag),
269 0);
270 gc_subroot_indexes_[tag] = entry->index();
271 return entry;
272}
273
274
275HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
276 const char* name,
277 SnapshotObjectId id,
278 int size) {
279 HeapEntry entry(this, type, name, id, size);
280 entries_.Add(entry);
281 return &entries_.last();
282}
283
284
285void HeapSnapshot::FillChildren() {
286 ASSERT(children().is_empty());
287 children().Allocate(edges().length());
288 int children_index = 0;
289 for (int i = 0; i < entries().length(); ++i) {
290 HeapEntry* entry = &entries()[i];
291 children_index = entry->set_children_index(children_index);
292 }
293 ASSERT(edges().length() == children_index);
294 for (int i = 0; i < edges().length(); ++i) {
295 HeapGraphEdge* edge = &edges()[i];
296 edge->ReplaceToIndexWithEntry(this);
297 edge->from()->add_child(edge);
298 }
299}
300
301
302class FindEntryById {
303 public:
304 explicit FindEntryById(SnapshotObjectId id) : id_(id) { }
305 int operator()(HeapEntry* const* entry) {
306 if ((*entry)->id() == id_) return 0;
307 return (*entry)->id() < id_ ? -1 : 1;
308 }
309 private:
310 SnapshotObjectId id_;
311};
312
313
314HeapEntry* HeapSnapshot::GetEntryById(SnapshotObjectId id) {
315 List<HeapEntry*>* entries_by_id = GetSortedEntriesList();
316 // Perform a binary search by id.
317 int index = SortedListBSearch(*entries_by_id, FindEntryById(id));
318 if (index == -1)
319 return NULL;
320 return entries_by_id->at(index);
321}
322
323
324template<class T>
325static int SortByIds(const T* entry1_ptr,
326 const T* entry2_ptr) {
327 if ((*entry1_ptr)->id() == (*entry2_ptr)->id()) return 0;
328 return (*entry1_ptr)->id() < (*entry2_ptr)->id() ? -1 : 1;
329}
330
331
332List<HeapEntry*>* HeapSnapshot::GetSortedEntriesList() {
333 if (sorted_entries_.is_empty()) {
334 sorted_entries_.Allocate(entries_.length());
335 for (int i = 0; i < entries_.length(); ++i) {
336 sorted_entries_[i] = &entries_[i];
337 }
338 sorted_entries_.Sort(SortByIds);
339 }
340 return &sorted_entries_;
341}
342
343
344void HeapSnapshot::Print(int max_depth) {
345 root()->Print("", "", max_depth, 0);
346}
347
348
349template<typename T, class P>
350static size_t GetMemoryUsedByList(const List<T, P>& list) {
351 return list.length() * sizeof(T) + sizeof(list);
352}
353
354
355size_t HeapSnapshot::RawSnapshotSize() const {
356 STATIC_CHECK(SnapshotSizeConstants<kPointerSize>::kExpectedHeapSnapshotSize ==
357 sizeof(HeapSnapshot)); // NOLINT
358 return
359 sizeof(*this) +
360 GetMemoryUsedByList(entries_) +
361 GetMemoryUsedByList(edges_) +
362 GetMemoryUsedByList(children_) +
363 GetMemoryUsedByList(sorted_entries_);
364}
365
366
367// We split IDs on evens for embedder objects (see
368// HeapObjectsMap::GenerateId) and odds for native objects.
369const SnapshotObjectId HeapObjectsMap::kInternalRootObjectId = 1;
370const SnapshotObjectId HeapObjectsMap::kGcRootsObjectId =
371 HeapObjectsMap::kInternalRootObjectId + HeapObjectsMap::kObjectIdStep;
372const SnapshotObjectId HeapObjectsMap::kGcRootsFirstSubrootId =
373 HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep;
374const SnapshotObjectId HeapObjectsMap::kFirstAvailableObjectId =
375 HeapObjectsMap::kGcRootsFirstSubrootId +
376 VisitorSynchronization::kNumberOfSyncTags * HeapObjectsMap::kObjectIdStep;
377
378HeapObjectsMap::HeapObjectsMap(Heap* heap)
379 : next_id_(kFirstAvailableObjectId),
380 entries_map_(AddressesMatch),
381 heap_(heap) {
382 // This dummy element solves a problem with entries_map_.
383 // When we do lookup in HashMap we see no difference between two cases:
384 // it has an entry with NULL as the value or it has created
385 // a new entry on the fly with NULL as the default value.
386 // With such dummy element we have a guaranty that all entries_map_ entries
387 // will have the value field grater than 0.
388 // This fact is using in MoveObject method.
389 entries_.Add(EntryInfo(0, NULL, 0));
390}
391
392
393void HeapObjectsMap::SnapshotGenerationFinished() {
394 RemoveDeadEntries();
395}
396
397
398void HeapObjectsMap::MoveObject(Address from, Address to) {
399 ASSERT(to != NULL);
400 ASSERT(from != NULL);
401 if (from == to) return;
402 void* from_value = entries_map_.Remove(from, AddressHash(from));
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +0000403 if (from_value == NULL) {
404 // It may occur that some untracked object moves to an address X and there
405 // is a tracked object at that address. In this case we should remove the
406 // entry as we know that the object has died.
407 void* to_value = entries_map_.Remove(to, AddressHash(to));
408 if (to_value != NULL) {
409 int to_entry_info_index =
410 static_cast<int>(reinterpret_cast<intptr_t>(to_value));
411 entries_.at(to_entry_info_index).addr = NULL;
412 }
413 } else {
414 HashMap::Entry* to_entry = entries_map_.Lookup(to, AddressHash(to), true);
415 if (to_entry->value != NULL) {
416 // We found the existing entry with to address for an old object.
417 // Without this operation we will have two EntryInfo's with the same
418 // value in addr field. It is bad because later at RemoveDeadEntries
419 // one of this entry will be removed with the corresponding entries_map_
420 // entry.
421 int to_entry_info_index =
422 static_cast<int>(reinterpret_cast<intptr_t>(to_entry->value));
423 entries_.at(to_entry_info_index).addr = NULL;
424 }
425 int from_entry_info_index =
426 static_cast<int>(reinterpret_cast<intptr_t>(from_value));
427 entries_.at(from_entry_info_index).addr = to;
428 to_entry->value = from_value;
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000429 }
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000430}
431
432
433SnapshotObjectId HeapObjectsMap::FindEntry(Address addr) {
434 HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), false);
435 if (entry == NULL) return 0;
436 int entry_index = static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
437 EntryInfo& entry_info = entries_.at(entry_index);
438 ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
439 return entry_info.id;
440}
441
442
443SnapshotObjectId HeapObjectsMap::FindOrAddEntry(Address addr,
444 unsigned int size) {
445 ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
446 HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), true);
447 if (entry->value != NULL) {
448 int entry_index =
449 static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
450 EntryInfo& entry_info = entries_.at(entry_index);
451 entry_info.accessed = true;
452 entry_info.size = size;
453 return entry_info.id;
454 }
455 entry->value = reinterpret_cast<void*>(entries_.length());
456 SnapshotObjectId id = next_id_;
457 next_id_ += kObjectIdStep;
458 entries_.Add(EntryInfo(id, addr, size));
459 ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
460 return id;
461}
462
463
464void HeapObjectsMap::StopHeapObjectsTracking() {
465 time_intervals_.Clear();
466}
467
468void HeapObjectsMap::UpdateHeapObjectsMap() {
469 HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask,
470 "HeapSnapshotsCollection::UpdateHeapObjectsMap");
471 HeapIterator iterator(heap_);
472 for (HeapObject* obj = iterator.next();
473 obj != NULL;
474 obj = iterator.next()) {
475 FindOrAddEntry(obj->address(), obj->Size());
476 }
477 RemoveDeadEntries();
478}
479
480
481SnapshotObjectId HeapObjectsMap::PushHeapObjectsStats(OutputStream* stream) {
482 UpdateHeapObjectsMap();
483 time_intervals_.Add(TimeInterval(next_id_));
484 int prefered_chunk_size = stream->GetChunkSize();
485 List<v8::HeapStatsUpdate> stats_buffer;
486 ASSERT(!entries_.is_empty());
487 EntryInfo* entry_info = &entries_.first();
488 EntryInfo* end_entry_info = &entries_.last() + 1;
489 for (int time_interval_index = 0;
490 time_interval_index < time_intervals_.length();
491 ++time_interval_index) {
492 TimeInterval& time_interval = time_intervals_[time_interval_index];
493 SnapshotObjectId time_interval_id = time_interval.id;
494 uint32_t entries_size = 0;
495 EntryInfo* start_entry_info = entry_info;
496 while (entry_info < end_entry_info && entry_info->id < time_interval_id) {
497 entries_size += entry_info->size;
498 ++entry_info;
499 }
500 uint32_t entries_count =
501 static_cast<uint32_t>(entry_info - start_entry_info);
502 if (time_interval.count != entries_count ||
503 time_interval.size != entries_size) {
504 stats_buffer.Add(v8::HeapStatsUpdate(
505 time_interval_index,
506 time_interval.count = entries_count,
507 time_interval.size = entries_size));
508 if (stats_buffer.length() >= prefered_chunk_size) {
509 OutputStream::WriteResult result = stream->WriteHeapStatsChunk(
510 &stats_buffer.first(), stats_buffer.length());
511 if (result == OutputStream::kAbort) return last_assigned_id();
512 stats_buffer.Clear();
513 }
514 }
515 }
516 ASSERT(entry_info == end_entry_info);
517 if (!stats_buffer.is_empty()) {
518 OutputStream::WriteResult result = stream->WriteHeapStatsChunk(
519 &stats_buffer.first(), stats_buffer.length());
520 if (result == OutputStream::kAbort) return last_assigned_id();
521 }
522 stream->EndOfStream();
523 return last_assigned_id();
524}
525
526
527void HeapObjectsMap::RemoveDeadEntries() {
528 ASSERT(entries_.length() > 0 &&
529 entries_.at(0).id == 0 &&
530 entries_.at(0).addr == NULL);
531 int first_free_entry = 1;
532 for (int i = 1; i < entries_.length(); ++i) {
533 EntryInfo& entry_info = entries_.at(i);
534 if (entry_info.accessed) {
535 if (first_free_entry != i) {
536 entries_.at(first_free_entry) = entry_info;
537 }
538 entries_.at(first_free_entry).accessed = false;
539 HashMap::Entry* entry = entries_map_.Lookup(
540 entry_info.addr, AddressHash(entry_info.addr), false);
541 ASSERT(entry);
542 entry->value = reinterpret_cast<void*>(first_free_entry);
543 ++first_free_entry;
544 } else {
545 if (entry_info.addr) {
546 entries_map_.Remove(entry_info.addr, AddressHash(entry_info.addr));
547 }
548 }
549 }
550 entries_.Rewind(first_free_entry);
551 ASSERT(static_cast<uint32_t>(entries_.length()) - 1 ==
552 entries_map_.occupancy());
553}
554
555
556SnapshotObjectId HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) {
557 SnapshotObjectId id = static_cast<SnapshotObjectId>(info->GetHash());
558 const char* label = info->GetLabel();
559 id ^= StringHasher::HashSequentialString(label,
560 static_cast<int>(strlen(label)),
561 HEAP->HashSeed());
562 intptr_t element_count = info->GetElementCount();
563 if (element_count != -1)
564 id ^= ComputeIntegerHash(static_cast<uint32_t>(element_count),
565 v8::internal::kZeroHashSeed);
566 return id << 1;
567}
568
569
570size_t HeapObjectsMap::GetUsedMemorySize() const {
571 return
572 sizeof(*this) +
573 sizeof(HashMap::Entry) * entries_map_.capacity() +
574 GetMemoryUsedByList(entries_) +
575 GetMemoryUsedByList(time_intervals_);
576}
577
578
579HeapSnapshotsCollection::HeapSnapshotsCollection(Heap* heap)
580 : is_tracking_objects_(false),
581 snapshots_uids_(HeapSnapshotsMatch),
582 token_enumerator_(new TokenEnumerator()),
583 ids_(heap) {
584}
585
586
587static void DeleteHeapSnapshot(HeapSnapshot** snapshot_ptr) {
588 delete *snapshot_ptr;
589}
590
591
592HeapSnapshotsCollection::~HeapSnapshotsCollection() {
593 delete token_enumerator_;
594 snapshots_.Iterate(DeleteHeapSnapshot);
595}
596
597
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +0000598HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(const char* name,
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000599 unsigned uid) {
600 is_tracking_objects_ = true; // Start watching for heap objects moves.
mstarzinger@chromium.orgf705b502013-04-04 11:38:09 +0000601 return new HeapSnapshot(this, name, uid);
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000602}
603
604
605void HeapSnapshotsCollection::SnapshotGenerationFinished(
606 HeapSnapshot* snapshot) {
607 ids_.SnapshotGenerationFinished();
608 if (snapshot != NULL) {
609 snapshots_.Add(snapshot);
610 HashMap::Entry* entry =
611 snapshots_uids_.Lookup(reinterpret_cast<void*>(snapshot->uid()),
612 static_cast<uint32_t>(snapshot->uid()),
613 true);
614 ASSERT(entry->value == NULL);
615 entry->value = snapshot;
616 }
617}
618
619
620HeapSnapshot* HeapSnapshotsCollection::GetSnapshot(unsigned uid) {
621 HashMap::Entry* entry = snapshots_uids_.Lookup(reinterpret_cast<void*>(uid),
622 static_cast<uint32_t>(uid),
623 false);
624 return entry != NULL ? reinterpret_cast<HeapSnapshot*>(entry->value) : NULL;
625}
626
627
628void HeapSnapshotsCollection::RemoveSnapshot(HeapSnapshot* snapshot) {
629 snapshots_.RemoveElement(snapshot);
630 unsigned uid = snapshot->uid();
631 snapshots_uids_.Remove(reinterpret_cast<void*>(uid),
632 static_cast<uint32_t>(uid));
633}
634
635
636Handle<HeapObject> HeapSnapshotsCollection::FindHeapObjectById(
637 SnapshotObjectId id) {
638 // First perform a full GC in order to avoid dead objects.
639 HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask,
640 "HeapSnapshotsCollection::FindHeapObjectById");
rossberg@chromium.org79e79022013-06-03 15:43:46 +0000641 DisallowHeapAllocation no_allocation;
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000642 HeapObject* object = NULL;
643 HeapIterator iterator(heap(), HeapIterator::kFilterUnreachable);
644 // Make sure that object with the given id is still reachable.
645 for (HeapObject* obj = iterator.next();
646 obj != NULL;
647 obj = iterator.next()) {
648 if (ids_.FindEntry(obj->address()) == id) {
649 ASSERT(object == NULL);
650 object = obj;
651 // Can't break -- kFilterUnreachable requires full heap traversal.
652 }
653 }
654 return object != NULL ? Handle<HeapObject>(object) : Handle<HeapObject>();
655}
656
657
658size_t HeapSnapshotsCollection::GetUsedMemorySize() const {
659 STATIC_CHECK(SnapshotSizeConstants<kPointerSize>::
660 kExpectedHeapSnapshotsCollectionSize ==
661 sizeof(HeapSnapshotsCollection)); // NOLINT
662 size_t size = sizeof(*this);
663 size += names_.GetUsedMemorySize();
664 size += ids_.GetUsedMemorySize();
665 size += sizeof(HashMap::Entry) * snapshots_uids_.capacity();
666 size += GetMemoryUsedByList(snapshots_);
667 for (int i = 0; i < snapshots_.length(); ++i) {
668 size += snapshots_[i]->RawSnapshotSize();
669 }
670 return size;
671}
672
673
674HeapEntriesMap::HeapEntriesMap()
675 : entries_(HeapThingsMatch) {
676}
677
678
679int HeapEntriesMap::Map(HeapThing thing) {
680 HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), false);
681 if (cache_entry == NULL) return HeapEntry::kNoEntry;
682 return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
683}
684
685
686void HeapEntriesMap::Pair(HeapThing thing, int entry) {
687 HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), true);
688 ASSERT(cache_entry->value == NULL);
689 cache_entry->value = reinterpret_cast<void*>(static_cast<intptr_t>(entry));
690}
691
692
693HeapObjectsSet::HeapObjectsSet()
694 : entries_(HeapEntriesMap::HeapThingsMatch) {
695}
696
697
698void HeapObjectsSet::Clear() {
699 entries_.Clear();
700}
701
702
703bool HeapObjectsSet::Contains(Object* obj) {
704 if (!obj->IsHeapObject()) return false;
705 HeapObject* object = HeapObject::cast(obj);
706 return entries_.Lookup(object, HeapEntriesMap::Hash(object), false) != NULL;
707}
708
709
710void HeapObjectsSet::Insert(Object* obj) {
711 if (!obj->IsHeapObject()) return;
712 HeapObject* object = HeapObject::cast(obj);
713 entries_.Lookup(object, HeapEntriesMap::Hash(object), true);
714}
715
716
717const char* HeapObjectsSet::GetTag(Object* obj) {
718 HeapObject* object = HeapObject::cast(obj);
719 HashMap::Entry* cache_entry =
720 entries_.Lookup(object, HeapEntriesMap::Hash(object), false);
721 return cache_entry != NULL
722 ? reinterpret_cast<const char*>(cache_entry->value)
723 : NULL;
724}
725
726
727void HeapObjectsSet::SetTag(Object* obj, const char* tag) {
728 if (!obj->IsHeapObject()) return;
729 HeapObject* object = HeapObject::cast(obj);
730 HashMap::Entry* cache_entry =
731 entries_.Lookup(object, HeapEntriesMap::Hash(object), true);
732 cache_entry->value = const_cast<char*>(tag);
733}
734
735
736HeapObject* const V8HeapExplorer::kInternalRootObject =
737 reinterpret_cast<HeapObject*>(
738 static_cast<intptr_t>(HeapObjectsMap::kInternalRootObjectId));
739HeapObject* const V8HeapExplorer::kGcRootsObject =
740 reinterpret_cast<HeapObject*>(
741 static_cast<intptr_t>(HeapObjectsMap::kGcRootsObjectId));
742HeapObject* const V8HeapExplorer::kFirstGcSubrootObject =
743 reinterpret_cast<HeapObject*>(
744 static_cast<intptr_t>(HeapObjectsMap::kGcRootsFirstSubrootId));
745HeapObject* const V8HeapExplorer::kLastGcSubrootObject =
746 reinterpret_cast<HeapObject*>(
747 static_cast<intptr_t>(HeapObjectsMap::kFirstAvailableObjectId));
748
749
750V8HeapExplorer::V8HeapExplorer(
751 HeapSnapshot* snapshot,
752 SnapshottingProgressReportingInterface* progress,
753 v8::HeapProfiler::ObjectNameResolver* resolver)
754 : heap_(Isolate::Current()->heap()),
755 snapshot_(snapshot),
756 collection_(snapshot_->collection()),
757 progress_(progress),
758 filler_(NULL),
759 global_object_name_resolver_(resolver) {
760}
761
762
763V8HeapExplorer::~V8HeapExplorer() {
764}
765
766
767HeapEntry* V8HeapExplorer::AllocateEntry(HeapThing ptr) {
768 return AddEntry(reinterpret_cast<HeapObject*>(ptr));
769}
770
771
772HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object) {
773 if (object == kInternalRootObject) {
774 snapshot_->AddRootEntry();
775 return snapshot_->root();
776 } else if (object == kGcRootsObject) {
777 HeapEntry* entry = snapshot_->AddGcRootsEntry();
778 return entry;
779 } else if (object >= kFirstGcSubrootObject && object < kLastGcSubrootObject) {
780 HeapEntry* entry = snapshot_->AddGcSubrootEntry(GetGcSubrootOrder(object));
781 return entry;
782 } else if (object->IsJSFunction()) {
783 JSFunction* func = JSFunction::cast(object);
784 SharedFunctionInfo* shared = func->shared();
785 const char* name = shared->bound() ? "native_bind" :
786 collection_->names()->GetName(String::cast(shared->name()));
787 return AddEntry(object, HeapEntry::kClosure, name);
788 } else if (object->IsJSRegExp()) {
789 JSRegExp* re = JSRegExp::cast(object);
790 return AddEntry(object,
791 HeapEntry::kRegExp,
792 collection_->names()->GetName(re->Pattern()));
793 } else if (object->IsJSObject()) {
794 const char* name = collection_->names()->GetName(
795 GetConstructorName(JSObject::cast(object)));
796 if (object->IsJSGlobalObject()) {
797 const char* tag = objects_tags_.GetTag(object);
798 if (tag != NULL) {
799 name = collection_->names()->GetFormatted("%s / %s", name, tag);
800 }
801 }
802 return AddEntry(object, HeapEntry::kObject, name);
803 } else if (object->IsString()) {
804 return AddEntry(object,
805 HeapEntry::kString,
806 collection_->names()->GetName(String::cast(object)));
807 } else if (object->IsCode()) {
808 return AddEntry(object, HeapEntry::kCode, "");
809 } else if (object->IsSharedFunctionInfo()) {
810 String* name = String::cast(SharedFunctionInfo::cast(object)->name());
811 return AddEntry(object,
812 HeapEntry::kCode,
813 collection_->names()->GetName(name));
814 } else if (object->IsScript()) {
815 Object* name = Script::cast(object)->name();
816 return AddEntry(object,
817 HeapEntry::kCode,
818 name->IsString()
819 ? collection_->names()->GetName(String::cast(name))
820 : "");
821 } else if (object->IsNativeContext()) {
822 return AddEntry(object, HeapEntry::kHidden, "system / NativeContext");
823 } else if (object->IsContext()) {
svenpanne@chromium.org876cca82013-03-18 14:43:20 +0000824 return AddEntry(object, HeapEntry::kObject, "system / Context");
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000825 } else if (object->IsFixedArray() ||
826 object->IsFixedDoubleArray() ||
827 object->IsByteArray() ||
828 object->IsExternalArray()) {
829 return AddEntry(object, HeapEntry::kArray, "");
830 } else if (object->IsHeapNumber()) {
831 return AddEntry(object, HeapEntry::kHeapNumber, "number");
832 }
833 return AddEntry(object, HeapEntry::kHidden, GetSystemEntryName(object));
834}
835
836
837HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
838 HeapEntry::Type type,
839 const char* name) {
840 int object_size = object->Size();
841 SnapshotObjectId object_id =
842 collection_->GetObjectId(object->address(), object_size);
843 return snapshot_->AddEntry(type, name, object_id, object_size);
844}
845
846
847class GcSubrootsEnumerator : public ObjectVisitor {
848 public:
849 GcSubrootsEnumerator(
850 SnapshotFillerInterface* filler, V8HeapExplorer* explorer)
851 : filler_(filler),
852 explorer_(explorer),
853 previous_object_count_(0),
854 object_count_(0) {
855 }
856 void VisitPointers(Object** start, Object** end) {
857 object_count_ += end - start;
858 }
859 void Synchronize(VisitorSynchronization::SyncTag tag) {
860 // Skip empty subroots.
861 if (previous_object_count_ != object_count_) {
862 previous_object_count_ = object_count_;
863 filler_->AddEntry(V8HeapExplorer::GetNthGcSubrootObject(tag), explorer_);
864 }
865 }
866 private:
867 SnapshotFillerInterface* filler_;
868 V8HeapExplorer* explorer_;
869 intptr_t previous_object_count_;
870 intptr_t object_count_;
871};
872
873
874void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) {
875 filler->AddEntry(kInternalRootObject, this);
876 filler->AddEntry(kGcRootsObject, this);
877 GcSubrootsEnumerator enumerator(filler, this);
878 heap_->IterateRoots(&enumerator, VISIT_ALL);
879}
880
881
882const char* V8HeapExplorer::GetSystemEntryName(HeapObject* object) {
883 switch (object->map()->instance_type()) {
884 case MAP_TYPE:
885 switch (Map::cast(object)->instance_type()) {
886#define MAKE_STRING_MAP_CASE(instance_type, size, name, Name) \
887 case instance_type: return "system / Map (" #Name ")";
888 STRING_TYPE_LIST(MAKE_STRING_MAP_CASE)
889#undef MAKE_STRING_MAP_CASE
890 default: return "system / Map";
891 }
danno@chromium.org41728482013-06-12 22:31:22 +0000892 case CELL_TYPE: return "system / Cell";
dslomov@chromium.orgb752d402013-06-18 11:54:54 +0000893 case PROPERTY_CELL_TYPE: return "system / PropertyCell";
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000894 case FOREIGN_TYPE: return "system / Foreign";
895 case ODDBALL_TYPE: return "system / Oddball";
896#define MAKE_STRUCT_CASE(NAME, Name, name) \
897 case NAME##_TYPE: return "system / "#Name;
898 STRUCT_LIST(MAKE_STRUCT_CASE)
899#undef MAKE_STRUCT_CASE
900 default: return "system";
901 }
902}
903
904
905int V8HeapExplorer::EstimateObjectsCount(HeapIterator* iterator) {
906 int objects_count = 0;
907 for (HeapObject* obj = iterator->next();
908 obj != NULL;
909 obj = iterator->next()) {
910 objects_count++;
911 }
912 return objects_count;
913}
914
915
916class IndexedReferencesExtractor : public ObjectVisitor {
917 public:
918 IndexedReferencesExtractor(V8HeapExplorer* generator,
919 HeapObject* parent_obj,
920 int parent)
921 : generator_(generator),
922 parent_obj_(parent_obj),
923 parent_(parent),
924 next_index_(1) {
925 }
926 void VisitPointers(Object** start, Object** end) {
927 for (Object** p = start; p < end; p++) {
928 if (CheckVisitedAndUnmark(p)) continue;
929 generator_->SetHiddenReference(parent_obj_, parent_, next_index_++, *p);
930 }
931 }
932 static void MarkVisitedField(HeapObject* obj, int offset) {
933 if (offset < 0) return;
934 Address field = obj->address() + offset;
935 ASSERT(!Memory::Object_at(field)->IsFailure());
936 ASSERT(Memory::Object_at(field)->IsHeapObject());
937 *field |= kFailureTag;
938 }
939
940 private:
941 bool CheckVisitedAndUnmark(Object** field) {
942 if ((*field)->IsFailure()) {
943 intptr_t untagged = reinterpret_cast<intptr_t>(*field) & ~kFailureTagMask;
944 *field = reinterpret_cast<Object*>(untagged | kHeapObjectTag);
945 ASSERT((*field)->IsHeapObject());
946 return true;
947 }
948 return false;
949 }
950 V8HeapExplorer* generator_;
951 HeapObject* parent_obj_;
952 int parent_;
953 int next_index_;
954};
955
956
957void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
958 HeapEntry* heap_entry = GetEntry(obj);
959 if (heap_entry == NULL) return; // No interest in this object.
960 int entry = heap_entry->index();
961
962 bool extract_indexed_refs = true;
963 if (obj->IsJSGlobalProxy()) {
964 ExtractJSGlobalProxyReferences(JSGlobalProxy::cast(obj));
965 } else if (obj->IsJSObject()) {
966 ExtractJSObjectReferences(entry, JSObject::cast(obj));
967 } else if (obj->IsString()) {
968 ExtractStringReferences(entry, String::cast(obj));
969 } else if (obj->IsContext()) {
970 ExtractContextReferences(entry, Context::cast(obj));
971 } else if (obj->IsMap()) {
972 ExtractMapReferences(entry, Map::cast(obj));
973 } else if (obj->IsSharedFunctionInfo()) {
974 ExtractSharedFunctionInfoReferences(entry, SharedFunctionInfo::cast(obj));
975 } else if (obj->IsScript()) {
976 ExtractScriptReferences(entry, Script::cast(obj));
977 } else if (obj->IsCodeCache()) {
978 ExtractCodeCacheReferences(entry, CodeCache::cast(obj));
979 } else if (obj->IsCode()) {
980 ExtractCodeReferences(entry, Code::cast(obj));
danno@chromium.org41728482013-06-12 22:31:22 +0000981 } else if (obj->IsCell()) {
982 ExtractCellReferences(entry, Cell::cast(obj));
983 extract_indexed_refs = false;
dslomov@chromium.orgb752d402013-06-18 11:54:54 +0000984 } else if (obj->IsPropertyCell()) {
985 ExtractPropertyCellReferences(
986 entry, PropertyCell::cast(obj));
ulan@chromium.org2e04b582013-02-21 14:06:02 +0000987 extract_indexed_refs = false;
988 }
989 if (extract_indexed_refs) {
990 SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset);
991 IndexedReferencesExtractor refs_extractor(this, obj, entry);
992 obj->Iterate(&refs_extractor);
993 }
994}
995
996
997void V8HeapExplorer::ExtractJSGlobalProxyReferences(JSGlobalProxy* proxy) {
998 // We need to reference JS global objects from snapshot's root.
999 // We use JSGlobalProxy because this is what embedder (e.g. browser)
1000 // uses for the global object.
1001 Object* object = proxy->map()->prototype();
1002 bool is_debug_object = false;
1003#ifdef ENABLE_DEBUGGER_SUPPORT
1004 is_debug_object = object->IsGlobalObject() &&
1005 Isolate::Current()->debug()->IsDebugGlobal(GlobalObject::cast(object));
1006#endif
1007 if (!is_debug_object) {
1008 SetUserGlobalReference(object);
1009 }
1010}
1011
1012
1013void V8HeapExplorer::ExtractJSObjectReferences(
1014 int entry, JSObject* js_obj) {
1015 HeapObject* obj = js_obj;
1016 ExtractClosureReferences(js_obj, entry);
1017 ExtractPropertyReferences(js_obj, entry);
1018 ExtractElementReferences(js_obj, entry);
1019 ExtractInternalReferences(js_obj, entry);
1020 SetPropertyReference(
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00001021 obj, entry, heap_->proto_string(), js_obj->GetPrototype());
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001022 if (obj->IsJSFunction()) {
1023 JSFunction* js_fun = JSFunction::cast(js_obj);
1024 Object* proto_or_map = js_fun->prototype_or_initial_map();
1025 if (!proto_or_map->IsTheHole()) {
1026 if (!proto_or_map->IsMap()) {
1027 SetPropertyReference(
1028 obj, entry,
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00001029 heap_->prototype_string(), proto_or_map,
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001030 NULL,
1031 JSFunction::kPrototypeOrInitialMapOffset);
1032 } else {
1033 SetPropertyReference(
1034 obj, entry,
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00001035 heap_->prototype_string(), js_fun->prototype());
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001036 }
1037 }
1038 SharedFunctionInfo* shared_info = js_fun->shared();
1039 // JSFunction has either bindings or literals and never both.
1040 bool bound = shared_info->bound();
1041 TagObject(js_fun->literals_or_bindings(),
1042 bound ? "(function bindings)" : "(function literals)");
1043 SetInternalReference(js_fun, entry,
1044 bound ? "bindings" : "literals",
1045 js_fun->literals_or_bindings(),
1046 JSFunction::kLiteralsOffset);
1047 TagObject(shared_info, "(shared function info)");
1048 SetInternalReference(js_fun, entry,
1049 "shared", shared_info,
1050 JSFunction::kSharedFunctionInfoOffset);
1051 TagObject(js_fun->unchecked_context(), "(context)");
1052 SetInternalReference(js_fun, entry,
1053 "context", js_fun->unchecked_context(),
1054 JSFunction::kContextOffset);
1055 for (int i = JSFunction::kNonWeakFieldsEndOffset;
1056 i < JSFunction::kSize;
1057 i += kPointerSize) {
1058 SetWeakReference(js_fun, entry, i, *HeapObject::RawField(js_fun, i), i);
1059 }
1060 } else if (obj->IsGlobalObject()) {
1061 GlobalObject* global_obj = GlobalObject::cast(obj);
1062 SetInternalReference(global_obj, entry,
1063 "builtins", global_obj->builtins(),
1064 GlobalObject::kBuiltinsOffset);
1065 SetInternalReference(global_obj, entry,
1066 "native_context", global_obj->native_context(),
1067 GlobalObject::kNativeContextOffset);
1068 SetInternalReference(global_obj, entry,
1069 "global_receiver", global_obj->global_receiver(),
1070 GlobalObject::kGlobalReceiverOffset);
1071 }
1072 TagObject(js_obj->properties(), "(object properties)");
1073 SetInternalReference(obj, entry,
1074 "properties", js_obj->properties(),
1075 JSObject::kPropertiesOffset);
1076 TagObject(js_obj->elements(), "(object elements)");
1077 SetInternalReference(obj, entry,
1078 "elements", js_obj->elements(),
1079 JSObject::kElementsOffset);
1080}
1081
1082
1083void V8HeapExplorer::ExtractStringReferences(int entry, String* string) {
1084 if (string->IsConsString()) {
1085 ConsString* cs = ConsString::cast(string);
1086 SetInternalReference(cs, entry, "first", cs->first(),
1087 ConsString::kFirstOffset);
1088 SetInternalReference(cs, entry, "second", cs->second(),
1089 ConsString::kSecondOffset);
1090 } else if (string->IsSlicedString()) {
1091 SlicedString* ss = SlicedString::cast(string);
1092 SetInternalReference(ss, entry, "parent", ss->parent(),
1093 SlicedString::kParentOffset);
1094 }
1095}
1096
1097
1098void V8HeapExplorer::ExtractContextReferences(int entry, Context* context) {
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +00001099 if (context == context->declaration_context()) {
1100 ScopeInfo* scope_info = context->closure()->shared()->scope_info();
1101 // Add context allocated locals.
1102 int context_locals = scope_info->ContextLocalCount();
1103 for (int i = 0; i < context_locals; ++i) {
1104 String* local_name = scope_info->ContextLocalName(i);
1105 int idx = Context::MIN_CONTEXT_SLOTS + i;
1106 SetContextReference(context, entry, local_name, context->get(idx),
1107 Context::OffsetOfElementAt(idx));
1108 }
1109 if (scope_info->HasFunctionName()) {
1110 String* name = scope_info->FunctionName();
1111 VariableMode mode;
1112 int idx = scope_info->FunctionContextSlotIndex(name, &mode);
1113 if (idx >= 0) {
1114 SetContextReference(context, entry, name, context->get(idx),
1115 Context::OffsetOfElementAt(idx));
1116 }
1117 }
1118 }
1119
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001120#define EXTRACT_CONTEXT_FIELD(index, type, name) \
1121 SetInternalReference(context, entry, #name, context->get(Context::index), \
1122 FixedArray::OffsetOfElementAt(Context::index));
1123 EXTRACT_CONTEXT_FIELD(CLOSURE_INDEX, JSFunction, closure);
1124 EXTRACT_CONTEXT_FIELD(PREVIOUS_INDEX, Context, previous);
1125 EXTRACT_CONTEXT_FIELD(EXTENSION_INDEX, Object, extension);
1126 EXTRACT_CONTEXT_FIELD(GLOBAL_OBJECT_INDEX, GlobalObject, global);
1127 if (context->IsNativeContext()) {
1128 TagObject(context->jsfunction_result_caches(),
1129 "(context func. result caches)");
1130 TagObject(context->normalized_map_cache(), "(context norm. map cache)");
1131 TagObject(context->runtime_context(), "(runtime context)");
1132 TagObject(context->embedder_data(), "(context data)");
1133 NATIVE_CONTEXT_FIELDS(EXTRACT_CONTEXT_FIELD);
1134#undef EXTRACT_CONTEXT_FIELD
1135 for (int i = Context::FIRST_WEAK_SLOT;
1136 i < Context::NATIVE_CONTEXT_SLOTS;
1137 ++i) {
1138 SetWeakReference(context, entry, i, context->get(i),
1139 FixedArray::OffsetOfElementAt(i));
1140 }
1141 }
1142}
1143
1144
1145void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) {
1146 SetInternalReference(map, entry,
1147 "prototype", map->prototype(), Map::kPrototypeOffset);
1148 SetInternalReference(map, entry,
1149 "constructor", map->constructor(),
1150 Map::kConstructorOffset);
1151 if (map->HasTransitionArray()) {
1152 TransitionArray* transitions = map->transitions();
1153
1154 Object* back_pointer = transitions->back_pointer_storage();
1155 TagObject(transitions->back_pointer_storage(), "(back pointer)");
1156 SetInternalReference(transitions, entry,
1157 "backpointer", back_pointer,
1158 TransitionArray::kBackPointerStorageOffset);
1159 IndexedReferencesExtractor transitions_refs(this, transitions, entry);
1160 transitions->Iterate(&transitions_refs);
1161
1162 TagObject(transitions, "(transition array)");
1163 SetInternalReference(map, entry,
1164 "transitions", transitions,
1165 Map::kTransitionsOrBackPointerOffset);
1166 } else {
1167 Object* back_pointer = map->GetBackPointer();
1168 TagObject(back_pointer, "(back pointer)");
1169 SetInternalReference(map, entry,
1170 "backpointer", back_pointer,
1171 Map::kTransitionsOrBackPointerOffset);
1172 }
1173 DescriptorArray* descriptors = map->instance_descriptors();
1174 TagObject(descriptors, "(map descriptors)");
1175 SetInternalReference(map, entry,
1176 "descriptors", descriptors,
1177 Map::kDescriptorsOffset);
1178
1179 SetInternalReference(map, entry,
1180 "code_cache", map->code_cache(),
1181 Map::kCodeCacheOffset);
1182}
1183
1184
1185void V8HeapExplorer::ExtractSharedFunctionInfoReferences(
1186 int entry, SharedFunctionInfo* shared) {
1187 HeapObject* obj = shared;
1188 SetInternalReference(obj, entry,
1189 "name", shared->name(),
1190 SharedFunctionInfo::kNameOffset);
1191 TagObject(shared->code(), "(code)");
1192 SetInternalReference(obj, entry,
1193 "code", shared->code(),
1194 SharedFunctionInfo::kCodeOffset);
1195 TagObject(shared->scope_info(), "(function scope info)");
1196 SetInternalReference(obj, entry,
1197 "scope_info", shared->scope_info(),
1198 SharedFunctionInfo::kScopeInfoOffset);
1199 SetInternalReference(obj, entry,
1200 "instance_class_name", shared->instance_class_name(),
1201 SharedFunctionInfo::kInstanceClassNameOffset);
1202 SetInternalReference(obj, entry,
1203 "script", shared->script(),
1204 SharedFunctionInfo::kScriptOffset);
1205 TagObject(shared->construct_stub(), "(code)");
1206 SetInternalReference(obj, entry,
1207 "construct_stub", shared->construct_stub(),
1208 SharedFunctionInfo::kConstructStubOffset);
1209 SetInternalReference(obj, entry,
1210 "function_data", shared->function_data(),
1211 SharedFunctionInfo::kFunctionDataOffset);
1212 SetInternalReference(obj, entry,
1213 "debug_info", shared->debug_info(),
1214 SharedFunctionInfo::kDebugInfoOffset);
1215 SetInternalReference(obj, entry,
1216 "inferred_name", shared->inferred_name(),
1217 SharedFunctionInfo::kInferredNameOffset);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001218 SetWeakReference(obj, entry,
1219 1, shared->initial_map(),
1220 SharedFunctionInfo::kInitialMapOffset);
1221}
1222
1223
1224void V8HeapExplorer::ExtractScriptReferences(int entry, Script* script) {
1225 HeapObject* obj = script;
1226 SetInternalReference(obj, entry,
1227 "source", script->source(),
1228 Script::kSourceOffset);
1229 SetInternalReference(obj, entry,
1230 "name", script->name(),
1231 Script::kNameOffset);
1232 SetInternalReference(obj, entry,
1233 "data", script->data(),
1234 Script::kDataOffset);
1235 SetInternalReference(obj, entry,
1236 "context_data", script->context_data(),
1237 Script::kContextOffset);
1238 TagObject(script->line_ends(), "(script line ends)");
1239 SetInternalReference(obj, entry,
1240 "line_ends", script->line_ends(),
1241 Script::kLineEndsOffset);
1242}
1243
1244
1245void V8HeapExplorer::ExtractCodeCacheReferences(
1246 int entry, CodeCache* code_cache) {
1247 TagObject(code_cache->default_cache(), "(default code cache)");
1248 SetInternalReference(code_cache, entry,
1249 "default_cache", code_cache->default_cache(),
1250 CodeCache::kDefaultCacheOffset);
1251 TagObject(code_cache->normal_type_cache(), "(code type cache)");
1252 SetInternalReference(code_cache, entry,
1253 "type_cache", code_cache->normal_type_cache(),
1254 CodeCache::kNormalTypeCacheOffset);
1255}
1256
1257
1258void V8HeapExplorer::ExtractCodeReferences(int entry, Code* code) {
1259 TagObject(code->relocation_info(), "(code relocation info)");
1260 SetInternalReference(code, entry,
1261 "relocation_info", code->relocation_info(),
1262 Code::kRelocationInfoOffset);
1263 SetInternalReference(code, entry,
1264 "handler_table", code->handler_table(),
1265 Code::kHandlerTableOffset);
1266 TagObject(code->deoptimization_data(), "(code deopt data)");
1267 SetInternalReference(code, entry,
1268 "deoptimization_data", code->deoptimization_data(),
1269 Code::kDeoptimizationDataOffset);
1270 if (code->kind() == Code::FUNCTION) {
1271 SetInternalReference(code, entry,
1272 "type_feedback_info", code->type_feedback_info(),
1273 Code::kTypeFeedbackInfoOffset);
1274 }
1275 SetInternalReference(code, entry,
1276 "gc_metadata", code->gc_metadata(),
1277 Code::kGCMetadataOffset);
1278}
1279
1280
danno@chromium.org41728482013-06-12 22:31:22 +00001281void V8HeapExplorer::ExtractCellReferences(int entry, Cell* cell) {
1282 SetInternalReference(cell, entry, "value", cell->value());
1283}
1284
1285
dslomov@chromium.orgb752d402013-06-18 11:54:54 +00001286void V8HeapExplorer::ExtractPropertyCellReferences(int entry,
1287 PropertyCell* cell) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001288 SetInternalReference(cell, entry, "value", cell->value());
danno@chromium.org41728482013-06-12 22:31:22 +00001289 SetInternalReference(cell, entry, "type", cell->type());
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001290}
1291
1292
1293void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj, int entry) {
1294 if (!js_obj->IsJSFunction()) return;
1295
1296 JSFunction* func = JSFunction::cast(js_obj);
1297 if (func->shared()->bound()) {
1298 FixedArray* bindings = func->function_bindings();
1299 SetNativeBindReference(js_obj, entry, "bound_this",
1300 bindings->get(JSFunction::kBoundThisIndex));
1301 SetNativeBindReference(js_obj, entry, "bound_function",
1302 bindings->get(JSFunction::kBoundFunctionIndex));
1303 for (int i = JSFunction::kBoundArgumentsStartIndex;
1304 i < bindings->length(); i++) {
1305 const char* reference_name = collection_->names()->GetFormatted(
1306 "bound_argument_%d",
1307 i - JSFunction::kBoundArgumentsStartIndex);
1308 SetNativeBindReference(js_obj, entry, reference_name,
1309 bindings->get(i));
1310 }
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001311 }
1312}
1313
1314
1315void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, int entry) {
1316 if (js_obj->HasFastProperties()) {
1317 DescriptorArray* descs = js_obj->map()->instance_descriptors();
1318 int real_size = js_obj->map()->NumberOfOwnDescriptors();
ulan@chromium.org57ff8812013-05-10 08:16:55 +00001319 for (int i = 0; i < real_size; i++) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001320 switch (descs->GetType(i)) {
1321 case FIELD: {
1322 int index = descs->GetFieldIndex(i);
1323
ulan@chromium.org750145a2013-03-07 15:14:13 +00001324 Name* k = descs->GetKey(i);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001325 if (index < js_obj->map()->inobject_properties()) {
1326 Object* value = js_obj->InObjectPropertyAt(index);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00001327 if (k != heap_->hidden_string()) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001328 SetPropertyReference(
1329 js_obj, entry,
1330 k, value,
1331 NULL,
1332 js_obj->GetInObjectPropertyOffset(index));
1333 } else {
1334 TagObject(value, "(hidden properties)");
1335 SetInternalReference(
1336 js_obj, entry,
1337 "hidden_properties", value,
1338 js_obj->GetInObjectPropertyOffset(index));
1339 }
1340 } else {
ulan@chromium.org57ff8812013-05-10 08:16:55 +00001341 Object* value = js_obj->RawFastPropertyAt(index);
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00001342 if (k != heap_->hidden_string()) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001343 SetPropertyReference(js_obj, entry, k, value);
1344 } else {
1345 TagObject(value, "(hidden properties)");
1346 SetInternalReference(js_obj, entry, "hidden_properties", value);
1347 }
1348 }
1349 break;
1350 }
1351 case CONSTANT_FUNCTION:
1352 SetPropertyReference(
1353 js_obj, entry,
1354 descs->GetKey(i), descs->GetConstantFunction(i));
1355 break;
1356 case CALLBACKS: {
1357 Object* callback_obj = descs->GetValue(i);
1358 if (callback_obj->IsAccessorPair()) {
1359 AccessorPair* accessors = AccessorPair::cast(callback_obj);
1360 if (Object* getter = accessors->getter()) {
1361 SetPropertyReference(js_obj, entry, descs->GetKey(i),
1362 getter, "get-%s");
1363 }
1364 if (Object* setter = accessors->setter()) {
1365 SetPropertyReference(js_obj, entry, descs->GetKey(i),
1366 setter, "set-%s");
1367 }
1368 }
1369 break;
1370 }
1371 case NORMAL: // only in slow mode
1372 case HANDLER: // only in lookup results, not in descriptors
1373 case INTERCEPTOR: // only in lookup results, not in descriptors
1374 break;
1375 case TRANSITION:
1376 case NONEXISTENT:
1377 UNREACHABLE();
1378 break;
1379 }
1380 }
1381 } else {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001382 NameDictionary* dictionary = js_obj->property_dictionary();
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001383 int length = dictionary->Capacity();
1384 for (int i = 0; i < length; ++i) {
1385 Object* k = dictionary->KeyAt(i);
1386 if (dictionary->IsKey(k)) {
1387 Object* target = dictionary->ValueAt(i);
1388 // We assume that global objects can only have slow properties.
dslomov@chromium.orgb752d402013-06-18 11:54:54 +00001389 Object* value = target->IsPropertyCell()
1390 ? PropertyCell::cast(target)->value()
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001391 : target;
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00001392 if (k != heap_->hidden_string()) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001393 SetPropertyReference(js_obj, entry, String::cast(k), value);
1394 } else {
1395 TagObject(value, "(hidden properties)");
1396 SetInternalReference(js_obj, entry, "hidden_properties", value);
1397 }
1398 }
1399 }
1400 }
1401}
1402
1403
1404void V8HeapExplorer::ExtractElementReferences(JSObject* js_obj, int entry) {
1405 if (js_obj->HasFastObjectElements()) {
1406 FixedArray* elements = FixedArray::cast(js_obj->elements());
1407 int length = js_obj->IsJSArray() ?
1408 Smi::cast(JSArray::cast(js_obj)->length())->value() :
1409 elements->length();
1410 for (int i = 0; i < length; ++i) {
1411 if (!elements->get(i)->IsTheHole()) {
1412 SetElementReference(js_obj, entry, i, elements->get(i));
1413 }
1414 }
1415 } else if (js_obj->HasDictionaryElements()) {
1416 SeededNumberDictionary* dictionary = js_obj->element_dictionary();
1417 int length = dictionary->Capacity();
1418 for (int i = 0; i < length; ++i) {
1419 Object* k = dictionary->KeyAt(i);
1420 if (dictionary->IsKey(k)) {
1421 ASSERT(k->IsNumber());
1422 uint32_t index = static_cast<uint32_t>(k->Number());
1423 SetElementReference(js_obj, entry, index, dictionary->ValueAt(i));
1424 }
1425 }
1426 }
1427}
1428
1429
1430void V8HeapExplorer::ExtractInternalReferences(JSObject* js_obj, int entry) {
1431 int length = js_obj->GetInternalFieldCount();
1432 for (int i = 0; i < length; ++i) {
1433 Object* o = js_obj->GetInternalField(i);
1434 SetInternalReference(
1435 js_obj, entry, i, o, js_obj->GetInternalFieldOffset(i));
1436 }
1437}
1438
1439
1440String* V8HeapExplorer::GetConstructorName(JSObject* object) {
1441 Heap* heap = object->GetHeap();
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00001442 if (object->IsJSFunction()) return heap->closure_string();
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001443 String* constructor_name = object->constructor_name();
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00001444 if (constructor_name == heap->Object_string()) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001445 // Look up an immediate "constructor" property, if it is a function,
1446 // return its name. This is for instances of binding objects, which
1447 // have prototype constructor type "Object".
1448 Object* constructor_prop = NULL;
1449 LookupResult result(heap->isolate());
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00001450 object->LocalLookupRealNamedProperty(heap->constructor_string(), &result);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001451 if (!result.IsFound()) return object->constructor_name();
1452
1453 constructor_prop = result.GetLazyValue();
1454 if (constructor_prop->IsJSFunction()) {
1455 Object* maybe_name =
1456 JSFunction::cast(constructor_prop)->shared()->name();
1457 if (maybe_name->IsString()) {
1458 String* name = String::cast(maybe_name);
1459 if (name->length() > 0) return name;
1460 }
1461 }
1462 }
1463 return object->constructor_name();
1464}
1465
1466
1467HeapEntry* V8HeapExplorer::GetEntry(Object* obj) {
1468 if (!obj->IsHeapObject()) return NULL;
1469 return filler_->FindOrAddEntry(obj, this);
1470}
1471
1472
1473class RootsReferencesExtractor : public ObjectVisitor {
1474 private:
1475 struct IndexTag {
1476 IndexTag(int index, VisitorSynchronization::SyncTag tag)
1477 : index(index), tag(tag) { }
1478 int index;
1479 VisitorSynchronization::SyncTag tag;
1480 };
1481
1482 public:
1483 RootsReferencesExtractor()
1484 : collecting_all_references_(false),
1485 previous_reference_count_(0) {
1486 }
1487
1488 void VisitPointers(Object** start, Object** end) {
1489 if (collecting_all_references_) {
1490 for (Object** p = start; p < end; p++) all_references_.Add(*p);
1491 } else {
1492 for (Object** p = start; p < end; p++) strong_references_.Add(*p);
1493 }
1494 }
1495
1496 void SetCollectingAllReferences() { collecting_all_references_ = true; }
1497
1498 void FillReferences(V8HeapExplorer* explorer) {
1499 ASSERT(strong_references_.length() <= all_references_.length());
1500 for (int i = 0; i < reference_tags_.length(); ++i) {
1501 explorer->SetGcRootsReference(reference_tags_[i].tag);
1502 }
1503 int strong_index = 0, all_index = 0, tags_index = 0;
1504 while (all_index < all_references_.length()) {
1505 if (strong_index < strong_references_.length() &&
1506 strong_references_[strong_index] == all_references_[all_index]) {
1507 explorer->SetGcSubrootReference(reference_tags_[tags_index].tag,
1508 false,
1509 all_references_[all_index++]);
1510 ++strong_index;
1511 } else {
1512 explorer->SetGcSubrootReference(reference_tags_[tags_index].tag,
1513 true,
1514 all_references_[all_index++]);
1515 }
1516 if (reference_tags_[tags_index].index == all_index) ++tags_index;
1517 }
1518 }
1519
1520 void Synchronize(VisitorSynchronization::SyncTag tag) {
1521 if (collecting_all_references_ &&
1522 previous_reference_count_ != all_references_.length()) {
1523 previous_reference_count_ = all_references_.length();
1524 reference_tags_.Add(IndexTag(previous_reference_count_, tag));
1525 }
1526 }
1527
1528 private:
1529 bool collecting_all_references_;
1530 List<Object*> strong_references_;
1531 List<Object*> all_references_;
1532 int previous_reference_count_;
1533 List<IndexTag> reference_tags_;
1534};
1535
1536
1537bool V8HeapExplorer::IterateAndExtractReferences(
1538 SnapshotFillerInterface* filler) {
1539 HeapIterator iterator(heap_, HeapIterator::kFilterUnreachable);
1540
1541 filler_ = filler;
1542 bool interrupted = false;
1543
1544 // Heap iteration with filtering must be finished in any case.
1545 for (HeapObject* obj = iterator.next();
1546 obj != NULL;
1547 obj = iterator.next(), progress_->ProgressStep()) {
1548 if (!interrupted) {
1549 ExtractReferences(obj);
1550 if (!progress_->ProgressReport(false)) interrupted = true;
1551 }
1552 }
1553 if (interrupted) {
1554 filler_ = NULL;
1555 return false;
1556 }
1557
1558 SetRootGcRootsReference();
1559 RootsReferencesExtractor extractor;
1560 heap_->IterateRoots(&extractor, VISIT_ONLY_STRONG);
1561 extractor.SetCollectingAllReferences();
1562 heap_->IterateRoots(&extractor, VISIT_ALL);
1563 extractor.FillReferences(this);
1564 filler_ = NULL;
1565 return progress_->ProgressReport(true);
1566}
1567
1568
1569bool V8HeapExplorer::IsEssentialObject(Object* object) {
1570 return object->IsHeapObject()
1571 && !object->IsOddball()
1572 && object != heap_->empty_byte_array()
1573 && object != heap_->empty_fixed_array()
1574 && object != heap_->empty_descriptor_array()
1575 && object != heap_->fixed_array_map()
danno@chromium.org41728482013-06-12 22:31:22 +00001576 && object != heap_->cell_map()
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001577 && object != heap_->global_property_cell_map()
1578 && object != heap_->shared_function_info_map()
1579 && object != heap_->free_space_map()
1580 && object != heap_->one_pointer_filler_map()
1581 && object != heap_->two_pointer_filler_map();
1582}
1583
1584
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +00001585void V8HeapExplorer::SetContextReference(HeapObject* parent_obj,
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001586 int parent_entry,
1587 String* reference_name,
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +00001588 Object* child_obj,
1589 int field_offset) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001590 HeapEntry* child_entry = GetEntry(child_obj);
1591 if (child_entry != NULL) {
1592 filler_->SetNamedReference(HeapGraphEdge::kContextVariable,
1593 parent_entry,
1594 collection_->names()->GetName(reference_name),
1595 child_entry);
svenpanne@chromium.org2bda5432013-03-15 12:39:50 +00001596 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001597 }
1598}
1599
1600
1601void V8HeapExplorer::SetNativeBindReference(HeapObject* parent_obj,
1602 int parent_entry,
1603 const char* reference_name,
1604 Object* child_obj) {
1605 HeapEntry* child_entry = GetEntry(child_obj);
1606 if (child_entry != NULL) {
1607 filler_->SetNamedReference(HeapGraphEdge::kShortcut,
1608 parent_entry,
1609 reference_name,
1610 child_entry);
1611 }
1612}
1613
1614
1615void V8HeapExplorer::SetElementReference(HeapObject* parent_obj,
1616 int parent_entry,
1617 int index,
1618 Object* child_obj) {
1619 HeapEntry* child_entry = GetEntry(child_obj);
1620 if (child_entry != NULL) {
1621 filler_->SetIndexedReference(HeapGraphEdge::kElement,
1622 parent_entry,
1623 index,
1624 child_entry);
1625 }
1626}
1627
1628
1629void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
1630 int parent_entry,
1631 const char* reference_name,
1632 Object* child_obj,
1633 int field_offset) {
1634 HeapEntry* child_entry = GetEntry(child_obj);
1635 if (child_entry == NULL) return;
1636 if (IsEssentialObject(child_obj)) {
1637 filler_->SetNamedReference(HeapGraphEdge::kInternal,
1638 parent_entry,
1639 reference_name,
1640 child_entry);
1641 }
1642 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
1643}
1644
1645
1646void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
1647 int parent_entry,
1648 int index,
1649 Object* child_obj,
1650 int field_offset) {
1651 HeapEntry* child_entry = GetEntry(child_obj);
1652 if (child_entry == NULL) return;
1653 if (IsEssentialObject(child_obj)) {
1654 filler_->SetNamedReference(HeapGraphEdge::kInternal,
1655 parent_entry,
1656 collection_->names()->GetName(index),
1657 child_entry);
1658 }
1659 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
1660}
1661
1662
1663void V8HeapExplorer::SetHiddenReference(HeapObject* parent_obj,
1664 int parent_entry,
1665 int index,
1666 Object* child_obj) {
1667 HeapEntry* child_entry = GetEntry(child_obj);
1668 if (child_entry != NULL && IsEssentialObject(child_obj)) {
1669 filler_->SetIndexedReference(HeapGraphEdge::kHidden,
1670 parent_entry,
1671 index,
1672 child_entry);
1673 }
1674}
1675
1676
1677void V8HeapExplorer::SetWeakReference(HeapObject* parent_obj,
1678 int parent_entry,
1679 int index,
1680 Object* child_obj,
1681 int field_offset) {
1682 HeapEntry* child_entry = GetEntry(child_obj);
1683 if (child_entry != NULL) {
1684 filler_->SetIndexedReference(HeapGraphEdge::kWeak,
1685 parent_entry,
1686 index,
1687 child_entry);
1688 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
1689 }
1690}
1691
1692
1693void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj,
1694 int parent_entry,
ulan@chromium.org750145a2013-03-07 15:14:13 +00001695 Name* reference_name,
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001696 Object* child_obj,
1697 const char* name_format_string,
1698 int field_offset) {
1699 HeapEntry* child_entry = GetEntry(child_obj);
1700 if (child_entry != NULL) {
ulan@chromium.org750145a2013-03-07 15:14:13 +00001701 HeapGraphEdge::Type type =
1702 reference_name->IsSymbol() || String::cast(reference_name)->length() > 0
1703 ? HeapGraphEdge::kProperty : HeapGraphEdge::kInternal;
1704 const char* name = name_format_string != NULL && reference_name->IsString()
1705 ? collection_->names()->GetFormatted(
1706 name_format_string,
1707 *String::cast(reference_name)->ToCString(
1708 DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL)) :
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001709 collection_->names()->GetName(reference_name);
1710
1711 filler_->SetNamedReference(type,
1712 parent_entry,
1713 name,
1714 child_entry);
1715 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
1716 }
1717}
1718
1719
1720void V8HeapExplorer::SetRootGcRootsReference() {
1721 filler_->SetIndexedAutoIndexReference(
1722 HeapGraphEdge::kElement,
1723 snapshot_->root()->index(),
1724 snapshot_->gc_roots());
1725}
1726
1727
1728void V8HeapExplorer::SetUserGlobalReference(Object* child_obj) {
1729 HeapEntry* child_entry = GetEntry(child_obj);
1730 ASSERT(child_entry != NULL);
1731 filler_->SetNamedAutoIndexReference(
1732 HeapGraphEdge::kShortcut,
1733 snapshot_->root()->index(),
1734 child_entry);
1735}
1736
1737
1738void V8HeapExplorer::SetGcRootsReference(VisitorSynchronization::SyncTag tag) {
1739 filler_->SetIndexedAutoIndexReference(
1740 HeapGraphEdge::kElement,
1741 snapshot_->gc_roots()->index(),
1742 snapshot_->gc_subroot(tag));
1743}
1744
1745
1746void V8HeapExplorer::SetGcSubrootReference(
1747 VisitorSynchronization::SyncTag tag, bool is_weak, Object* child_obj) {
1748 HeapEntry* child_entry = GetEntry(child_obj);
1749 if (child_entry != NULL) {
1750 const char* name = GetStrongGcSubrootName(child_obj);
1751 if (name != NULL) {
1752 filler_->SetNamedReference(
1753 HeapGraphEdge::kInternal,
1754 snapshot_->gc_subroot(tag)->index(),
1755 name,
1756 child_entry);
1757 } else {
1758 filler_->SetIndexedAutoIndexReference(
1759 is_weak ? HeapGraphEdge::kWeak : HeapGraphEdge::kElement,
1760 snapshot_->gc_subroot(tag)->index(),
1761 child_entry);
1762 }
1763 }
1764}
1765
1766
1767const char* V8HeapExplorer::GetStrongGcSubrootName(Object* object) {
1768 if (strong_gc_subroot_names_.is_empty()) {
1769#define NAME_ENTRY(name) strong_gc_subroot_names_.SetTag(heap_->name(), #name);
1770#define ROOT_NAME(type, name, camel_name) NAME_ENTRY(name)
1771 STRONG_ROOT_LIST(ROOT_NAME)
1772#undef ROOT_NAME
1773#define STRUCT_MAP_NAME(NAME, Name, name) NAME_ENTRY(name##_map)
1774 STRUCT_LIST(STRUCT_MAP_NAME)
1775#undef STRUCT_MAP_NAME
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +00001776#define STRING_NAME(name, str) NAME_ENTRY(name)
1777 INTERNALIZED_STRING_LIST(STRING_NAME)
1778#undef STRING_NAME
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001779#undef NAME_ENTRY
1780 CHECK(!strong_gc_subroot_names_.is_empty());
1781 }
1782 return strong_gc_subroot_names_.GetTag(object);
1783}
1784
1785
1786void V8HeapExplorer::TagObject(Object* obj, const char* tag) {
1787 if (IsEssentialObject(obj)) {
1788 HeapEntry* entry = GetEntry(obj);
1789 if (entry->name()[0] == '\0') {
1790 entry->set_name(tag);
1791 }
1792 }
1793}
1794
1795
1796class GlobalObjectsEnumerator : public ObjectVisitor {
1797 public:
1798 virtual void VisitPointers(Object** start, Object** end) {
1799 for (Object** p = start; p < end; p++) {
1800 if ((*p)->IsNativeContext()) {
1801 Context* context = Context::cast(*p);
1802 JSObject* proxy = context->global_proxy();
1803 if (proxy->IsJSGlobalProxy()) {
1804 Object* global = proxy->map()->prototype();
1805 if (global->IsJSGlobalObject()) {
1806 objects_.Add(Handle<JSGlobalObject>(JSGlobalObject::cast(global)));
1807 }
1808 }
1809 }
1810 }
1811 }
1812 int count() { return objects_.length(); }
1813 Handle<JSGlobalObject>& at(int i) { return objects_[i]; }
1814
1815 private:
1816 List<Handle<JSGlobalObject> > objects_;
1817};
1818
1819
1820// Modifies heap. Must not be run during heap traversal.
1821void V8HeapExplorer::TagGlobalObjects() {
1822 Isolate* isolate = Isolate::Current();
1823 HandleScope scope(isolate);
1824 GlobalObjectsEnumerator enumerator;
1825 isolate->global_handles()->IterateAllRoots(&enumerator);
1826 const char** urls = NewArray<const char*>(enumerator.count());
1827 for (int i = 0, l = enumerator.count(); i < l; ++i) {
1828 if (global_object_name_resolver_) {
1829 HandleScope scope(isolate);
1830 Handle<JSGlobalObject> global_obj = enumerator.at(i);
1831 urls[i] = global_object_name_resolver_->GetName(
1832 Utils::ToLocal(Handle<JSObject>::cast(global_obj)));
1833 } else {
1834 urls[i] = NULL;
1835 }
1836 }
1837
rossberg@chromium.org79e79022013-06-03 15:43:46 +00001838 DisallowHeapAllocation no_allocation;
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001839 for (int i = 0, l = enumerator.count(); i < l; ++i) {
1840 objects_tags_.SetTag(*enumerator.at(i), urls[i]);
1841 }
1842
1843 DeleteArray(urls);
1844}
1845
1846
1847class GlobalHandlesExtractor : public ObjectVisitor {
1848 public:
1849 explicit GlobalHandlesExtractor(NativeObjectsExplorer* explorer)
1850 : explorer_(explorer) {}
1851 virtual ~GlobalHandlesExtractor() {}
1852 virtual void VisitPointers(Object** start, Object** end) {
1853 UNREACHABLE();
1854 }
1855 virtual void VisitEmbedderReference(Object** p, uint16_t class_id) {
1856 explorer_->VisitSubtreeWrapper(p, class_id);
1857 }
1858 private:
1859 NativeObjectsExplorer* explorer_;
1860};
1861
1862
1863class BasicHeapEntriesAllocator : public HeapEntriesAllocator {
1864 public:
1865 BasicHeapEntriesAllocator(
1866 HeapSnapshot* snapshot,
1867 HeapEntry::Type entries_type)
1868 : snapshot_(snapshot),
1869 collection_(snapshot_->collection()),
1870 entries_type_(entries_type) {
1871 }
1872 virtual HeapEntry* AllocateEntry(HeapThing ptr);
1873 private:
1874 HeapSnapshot* snapshot_;
1875 HeapSnapshotsCollection* collection_;
1876 HeapEntry::Type entries_type_;
1877};
1878
1879
1880HeapEntry* BasicHeapEntriesAllocator::AllocateEntry(HeapThing ptr) {
1881 v8::RetainedObjectInfo* info = reinterpret_cast<v8::RetainedObjectInfo*>(ptr);
1882 intptr_t elements = info->GetElementCount();
1883 intptr_t size = info->GetSizeInBytes();
1884 const char* name = elements != -1
1885 ? collection_->names()->GetFormatted(
1886 "%s / %" V8_PTR_PREFIX "d entries", info->GetLabel(), elements)
1887 : collection_->names()->GetCopy(info->GetLabel());
1888 return snapshot_->AddEntry(
1889 entries_type_,
1890 name,
1891 HeapObjectsMap::GenerateId(info),
1892 size != -1 ? static_cast<int>(size) : 0);
1893}
1894
1895
1896NativeObjectsExplorer::NativeObjectsExplorer(
1897 HeapSnapshot* snapshot, SnapshottingProgressReportingInterface* progress)
1898 : snapshot_(snapshot),
1899 collection_(snapshot_->collection()),
1900 progress_(progress),
1901 embedder_queried_(false),
1902 objects_by_info_(RetainedInfosMatch),
1903 native_groups_(StringsMatch),
1904 filler_(NULL) {
1905 synthetic_entries_allocator_ =
1906 new BasicHeapEntriesAllocator(snapshot, HeapEntry::kSynthetic);
1907 native_entries_allocator_ =
1908 new BasicHeapEntriesAllocator(snapshot, HeapEntry::kNative);
1909}
1910
1911
1912NativeObjectsExplorer::~NativeObjectsExplorer() {
1913 for (HashMap::Entry* p = objects_by_info_.Start();
1914 p != NULL;
1915 p = objects_by_info_.Next(p)) {
1916 v8::RetainedObjectInfo* info =
1917 reinterpret_cast<v8::RetainedObjectInfo*>(p->key);
1918 info->Dispose();
1919 List<HeapObject*>* objects =
1920 reinterpret_cast<List<HeapObject*>* >(p->value);
1921 delete objects;
1922 }
1923 for (HashMap::Entry* p = native_groups_.Start();
1924 p != NULL;
1925 p = native_groups_.Next(p)) {
1926 v8::RetainedObjectInfo* info =
1927 reinterpret_cast<v8::RetainedObjectInfo*>(p->value);
1928 info->Dispose();
1929 }
1930 delete synthetic_entries_allocator_;
1931 delete native_entries_allocator_;
1932}
1933
1934
1935int NativeObjectsExplorer::EstimateObjectsCount() {
1936 FillRetainedObjects();
1937 return objects_by_info_.occupancy();
1938}
1939
1940
1941void NativeObjectsExplorer::FillRetainedObjects() {
1942 if (embedder_queried_) return;
1943 Isolate* isolate = Isolate::Current();
1944 const GCType major_gc_type = kGCTypeMarkSweepCompact;
1945 // Record objects that are joined into ObjectGroups.
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001946 isolate->heap()->CallGCPrologueCallbacks(
1947 major_gc_type, kGCCallbackFlagConstructRetainedObjectInfos);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001948 List<ObjectGroup*>* groups = isolate->global_handles()->object_groups();
1949 for (int i = 0; i < groups->length(); ++i) {
1950 ObjectGroup* group = groups->at(i);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001951 if (group->info == NULL) continue;
1952 List<HeapObject*>* list = GetListMaybeDisposeInfo(group->info);
1953 for (size_t j = 0; j < group->length; ++j) {
1954 HeapObject* obj = HeapObject::cast(*group->objects[j]);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001955 list->Add(obj);
1956 in_groups_.Insert(obj);
1957 }
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001958 group->info = NULL; // Acquire info object ownership.
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001959 }
1960 isolate->global_handles()->RemoveObjectGroups();
1961 isolate->heap()->CallGCEpilogueCallbacks(major_gc_type);
1962 // Record objects that are not in ObjectGroups, but have class ID.
1963 GlobalHandlesExtractor extractor(this);
1964 isolate->global_handles()->IterateAllRootsWithClassIds(&extractor);
1965 embedder_queried_ = true;
1966}
1967
1968void NativeObjectsExplorer::FillImplicitReferences() {
1969 Isolate* isolate = Isolate::Current();
1970 List<ImplicitRefGroup*>* groups =
1971 isolate->global_handles()->implicit_ref_groups();
1972 for (int i = 0; i < groups->length(); ++i) {
1973 ImplicitRefGroup* group = groups->at(i);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001974 HeapObject* parent = *group->parent;
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001975 int parent_entry =
1976 filler_->FindOrAddEntry(parent, native_entries_allocator_)->index();
1977 ASSERT(parent_entry != HeapEntry::kNoEntry);
danno@chromium.orgca29dd82013-04-26 11:59:48 +00001978 Object*** children = group->children;
1979 for (size_t j = 0; j < group->length; ++j) {
ulan@chromium.org2e04b582013-02-21 14:06:02 +00001980 Object* child = *children[j];
1981 HeapEntry* child_entry =
1982 filler_->FindOrAddEntry(child, native_entries_allocator_);
1983 filler_->SetNamedReference(
1984 HeapGraphEdge::kInternal,
1985 parent_entry,
1986 "native",
1987 child_entry);
1988 }
1989 }
1990 isolate->global_handles()->RemoveImplicitRefGroups();
1991}
1992
1993List<HeapObject*>* NativeObjectsExplorer::GetListMaybeDisposeInfo(
1994 v8::RetainedObjectInfo* info) {
1995 HashMap::Entry* entry =
1996 objects_by_info_.Lookup(info, InfoHash(info), true);
1997 if (entry->value != NULL) {
1998 info->Dispose();
1999 } else {
2000 entry->value = new List<HeapObject*>(4);
2001 }
2002 return reinterpret_cast<List<HeapObject*>* >(entry->value);
2003}
2004
2005
2006bool NativeObjectsExplorer::IterateAndExtractReferences(
2007 SnapshotFillerInterface* filler) {
2008 filler_ = filler;
2009 FillRetainedObjects();
2010 FillImplicitReferences();
2011 if (EstimateObjectsCount() > 0) {
2012 for (HashMap::Entry* p = objects_by_info_.Start();
2013 p != NULL;
2014 p = objects_by_info_.Next(p)) {
2015 v8::RetainedObjectInfo* info =
2016 reinterpret_cast<v8::RetainedObjectInfo*>(p->key);
2017 SetNativeRootReference(info);
2018 List<HeapObject*>* objects =
2019 reinterpret_cast<List<HeapObject*>* >(p->value);
2020 for (int i = 0; i < objects->length(); ++i) {
2021 SetWrapperNativeReferences(objects->at(i), info);
2022 }
2023 }
2024 SetRootNativeRootsReference();
2025 }
2026 filler_ = NULL;
2027 return true;
2028}
2029
2030
2031class NativeGroupRetainedObjectInfo : public v8::RetainedObjectInfo {
2032 public:
2033 explicit NativeGroupRetainedObjectInfo(const char* label)
2034 : disposed_(false),
2035 hash_(reinterpret_cast<intptr_t>(label)),
2036 label_(label) {
2037 }
2038
2039 virtual ~NativeGroupRetainedObjectInfo() {}
2040 virtual void Dispose() {
2041 CHECK(!disposed_);
2042 disposed_ = true;
2043 delete this;
2044 }
2045 virtual bool IsEquivalent(RetainedObjectInfo* other) {
2046 return hash_ == other->GetHash() && !strcmp(label_, other->GetLabel());
2047 }
2048 virtual intptr_t GetHash() { return hash_; }
2049 virtual const char* GetLabel() { return label_; }
2050
2051 private:
2052 bool disposed_;
2053 intptr_t hash_;
2054 const char* label_;
2055};
2056
2057
2058NativeGroupRetainedObjectInfo* NativeObjectsExplorer::FindOrAddGroupInfo(
2059 const char* label) {
2060 const char* label_copy = collection_->names()->GetCopy(label);
2061 uint32_t hash = StringHasher::HashSequentialString(
2062 label_copy,
2063 static_cast<int>(strlen(label_copy)),
2064 HEAP->HashSeed());
2065 HashMap::Entry* entry = native_groups_.Lookup(const_cast<char*>(label_copy),
2066 hash, true);
2067 if (entry->value == NULL) {
2068 entry->value = new NativeGroupRetainedObjectInfo(label);
2069 }
2070 return static_cast<NativeGroupRetainedObjectInfo*>(entry->value);
2071}
2072
2073
2074void NativeObjectsExplorer::SetNativeRootReference(
2075 v8::RetainedObjectInfo* info) {
2076 HeapEntry* child_entry =
2077 filler_->FindOrAddEntry(info, native_entries_allocator_);
2078 ASSERT(child_entry != NULL);
2079 NativeGroupRetainedObjectInfo* group_info =
2080 FindOrAddGroupInfo(info->GetGroupLabel());
2081 HeapEntry* group_entry =
2082 filler_->FindOrAddEntry(group_info, synthetic_entries_allocator_);
2083 filler_->SetNamedAutoIndexReference(
2084 HeapGraphEdge::kInternal,
2085 group_entry->index(),
2086 child_entry);
2087}
2088
2089
2090void NativeObjectsExplorer::SetWrapperNativeReferences(
2091 HeapObject* wrapper, v8::RetainedObjectInfo* info) {
2092 HeapEntry* wrapper_entry = filler_->FindEntry(wrapper);
2093 ASSERT(wrapper_entry != NULL);
2094 HeapEntry* info_entry =
2095 filler_->FindOrAddEntry(info, native_entries_allocator_);
2096 ASSERT(info_entry != NULL);
2097 filler_->SetNamedReference(HeapGraphEdge::kInternal,
2098 wrapper_entry->index(),
2099 "native",
2100 info_entry);
2101 filler_->SetIndexedAutoIndexReference(HeapGraphEdge::kElement,
2102 info_entry->index(),
2103 wrapper_entry);
2104}
2105
2106
2107void NativeObjectsExplorer::SetRootNativeRootsReference() {
2108 for (HashMap::Entry* entry = native_groups_.Start();
2109 entry;
2110 entry = native_groups_.Next(entry)) {
2111 NativeGroupRetainedObjectInfo* group_info =
2112 static_cast<NativeGroupRetainedObjectInfo*>(entry->value);
2113 HeapEntry* group_entry =
2114 filler_->FindOrAddEntry(group_info, native_entries_allocator_);
2115 ASSERT(group_entry != NULL);
2116 filler_->SetIndexedAutoIndexReference(
2117 HeapGraphEdge::kElement,
2118 snapshot_->root()->index(),
2119 group_entry);
2120 }
2121}
2122
2123
2124void NativeObjectsExplorer::VisitSubtreeWrapper(Object** p, uint16_t class_id) {
2125 if (in_groups_.Contains(*p)) return;
2126 Isolate* isolate = Isolate::Current();
2127 v8::RetainedObjectInfo* info =
2128 isolate->heap_profiler()->ExecuteWrapperClassCallback(class_id, p);
2129 if (info == NULL) return;
2130 GetListMaybeDisposeInfo(info)->Add(HeapObject::cast(*p));
2131}
2132
2133
2134class SnapshotFiller : public SnapshotFillerInterface {
2135 public:
2136 explicit SnapshotFiller(HeapSnapshot* snapshot, HeapEntriesMap* entries)
2137 : snapshot_(snapshot),
2138 collection_(snapshot->collection()),
2139 entries_(entries) { }
2140 HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
2141 HeapEntry* entry = allocator->AllocateEntry(ptr);
2142 entries_->Pair(ptr, entry->index());
2143 return entry;
2144 }
2145 HeapEntry* FindEntry(HeapThing ptr) {
2146 int index = entries_->Map(ptr);
2147 return index != HeapEntry::kNoEntry ? &snapshot_->entries()[index] : NULL;
2148 }
2149 HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
2150 HeapEntry* entry = FindEntry(ptr);
2151 return entry != NULL ? entry : AddEntry(ptr, allocator);
2152 }
2153 void SetIndexedReference(HeapGraphEdge::Type type,
2154 int parent,
2155 int index,
2156 HeapEntry* child_entry) {
2157 HeapEntry* parent_entry = &snapshot_->entries()[parent];
2158 parent_entry->SetIndexedReference(type, index, child_entry);
2159 }
2160 void SetIndexedAutoIndexReference(HeapGraphEdge::Type type,
2161 int parent,
2162 HeapEntry* child_entry) {
2163 HeapEntry* parent_entry = &snapshot_->entries()[parent];
2164 int index = parent_entry->children_count() + 1;
2165 parent_entry->SetIndexedReference(type, index, child_entry);
2166 }
2167 void SetNamedReference(HeapGraphEdge::Type type,
2168 int parent,
2169 const char* reference_name,
2170 HeapEntry* child_entry) {
2171 HeapEntry* parent_entry = &snapshot_->entries()[parent];
2172 parent_entry->SetNamedReference(type, reference_name, child_entry);
2173 }
2174 void SetNamedAutoIndexReference(HeapGraphEdge::Type type,
2175 int parent,
2176 HeapEntry* child_entry) {
2177 HeapEntry* parent_entry = &snapshot_->entries()[parent];
2178 int index = parent_entry->children_count() + 1;
2179 parent_entry->SetNamedReference(
2180 type,
2181 collection_->names()->GetName(index),
2182 child_entry);
2183 }
2184
2185 private:
2186 HeapSnapshot* snapshot_;
2187 HeapSnapshotsCollection* collection_;
2188 HeapEntriesMap* entries_;
2189};
2190
2191
2192HeapSnapshotGenerator::HeapSnapshotGenerator(
2193 HeapSnapshot* snapshot,
2194 v8::ActivityControl* control,
2195 v8::HeapProfiler::ObjectNameResolver* resolver,
2196 Heap* heap)
2197 : snapshot_(snapshot),
2198 control_(control),
2199 v8_heap_explorer_(snapshot_, this, resolver),
2200 dom_explorer_(snapshot_, this),
2201 heap_(heap) {
2202}
2203
2204
2205bool HeapSnapshotGenerator::GenerateSnapshot() {
2206 v8_heap_explorer_.TagGlobalObjects();
2207
2208 // TODO(1562) Profiler assumes that any object that is in the heap after
2209 // full GC is reachable from the root when computing dominators.
2210 // This is not true for weakly reachable objects.
2211 // As a temporary solution we call GC twice.
2212 Isolate::Current()->heap()->CollectAllGarbage(
2213 Heap::kMakeHeapIterableMask,
2214 "HeapSnapshotGenerator::GenerateSnapshot");
2215 Isolate::Current()->heap()->CollectAllGarbage(
2216 Heap::kMakeHeapIterableMask,
2217 "HeapSnapshotGenerator::GenerateSnapshot");
2218
2219#ifdef VERIFY_HEAP
2220 Heap* debug_heap = Isolate::Current()->heap();
2221 CHECK(!debug_heap->old_data_space()->was_swept_conservatively());
2222 CHECK(!debug_heap->old_pointer_space()->was_swept_conservatively());
2223 CHECK(!debug_heap->code_space()->was_swept_conservatively());
2224 CHECK(!debug_heap->cell_space()->was_swept_conservatively());
danno@chromium.org41728482013-06-12 22:31:22 +00002225 CHECK(!debug_heap->property_cell_space()->
2226 was_swept_conservatively());
ulan@chromium.org2e04b582013-02-21 14:06:02 +00002227 CHECK(!debug_heap->map_space()->was_swept_conservatively());
2228#endif
2229
2230 // The following code uses heap iterators, so we want the heap to be
2231 // stable. It should follow TagGlobalObjects as that can allocate.
rossberg@chromium.org79e79022013-06-03 15:43:46 +00002232 DisallowHeapAllocation no_alloc;
ulan@chromium.org2e04b582013-02-21 14:06:02 +00002233
2234#ifdef VERIFY_HEAP
2235 debug_heap->Verify();
2236#endif
2237
2238 SetProgressTotal(1); // 1 pass.
2239
2240#ifdef VERIFY_HEAP
2241 debug_heap->Verify();
2242#endif
2243
2244 if (!FillReferences()) return false;
2245
2246 snapshot_->FillChildren();
2247 snapshot_->RememberLastJSObjectId();
2248
2249 progress_counter_ = progress_total_;
2250 if (!ProgressReport(true)) return false;
2251 return true;
2252}
2253
2254
2255void HeapSnapshotGenerator::ProgressStep() {
2256 ++progress_counter_;
2257}
2258
2259
2260bool HeapSnapshotGenerator::ProgressReport(bool force) {
2261 const int kProgressReportGranularity = 10000;
2262 if (control_ != NULL
2263 && (force || progress_counter_ % kProgressReportGranularity == 0)) {
2264 return
2265 control_->ReportProgressValue(progress_counter_, progress_total_) ==
2266 v8::ActivityControl::kContinue;
2267 }
2268 return true;
2269}
2270
2271
2272void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) {
2273 if (control_ == NULL) return;
2274 HeapIterator iterator(heap_, HeapIterator::kFilterUnreachable);
2275 progress_total_ = iterations_count * (
2276 v8_heap_explorer_.EstimateObjectsCount(&iterator) +
2277 dom_explorer_.EstimateObjectsCount());
2278 progress_counter_ = 0;
2279}
2280
2281
2282bool HeapSnapshotGenerator::FillReferences() {
2283 SnapshotFiller filler(snapshot_, &entries_);
2284 v8_heap_explorer_.AddRootEntries(&filler);
2285 return v8_heap_explorer_.IterateAndExtractReferences(&filler)
2286 && dom_explorer_.IterateAndExtractReferences(&filler);
2287}
2288
2289
2290template<int bytes> struct MaxDecimalDigitsIn;
2291template<> struct MaxDecimalDigitsIn<4> {
2292 static const int kSigned = 11;
2293 static const int kUnsigned = 10;
2294};
2295template<> struct MaxDecimalDigitsIn<8> {
2296 static const int kSigned = 20;
2297 static const int kUnsigned = 20;
2298};
2299
2300
2301class OutputStreamWriter {
2302 public:
2303 explicit OutputStreamWriter(v8::OutputStream* stream)
2304 : stream_(stream),
2305 chunk_size_(stream->GetChunkSize()),
2306 chunk_(chunk_size_),
2307 chunk_pos_(0),
2308 aborted_(false) {
2309 ASSERT(chunk_size_ > 0);
2310 }
2311 bool aborted() { return aborted_; }
2312 void AddCharacter(char c) {
2313 ASSERT(c != '\0');
2314 ASSERT(chunk_pos_ < chunk_size_);
2315 chunk_[chunk_pos_++] = c;
2316 MaybeWriteChunk();
2317 }
2318 void AddString(const char* s) {
2319 AddSubstring(s, StrLength(s));
2320 }
2321 void AddSubstring(const char* s, int n) {
2322 if (n <= 0) return;
2323 ASSERT(static_cast<size_t>(n) <= strlen(s));
2324 const char* s_end = s + n;
2325 while (s < s_end) {
2326 int s_chunk_size = Min(
2327 chunk_size_ - chunk_pos_, static_cast<int>(s_end - s));
2328 ASSERT(s_chunk_size > 0);
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +00002329 OS::MemCopy(chunk_.start() + chunk_pos_, s, s_chunk_size);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00002330 s += s_chunk_size;
2331 chunk_pos_ += s_chunk_size;
2332 MaybeWriteChunk();
2333 }
2334 }
2335 void AddNumber(unsigned n) { AddNumberImpl<unsigned>(n, "%u"); }
2336 void Finalize() {
2337 if (aborted_) return;
2338 ASSERT(chunk_pos_ < chunk_size_);
2339 if (chunk_pos_ != 0) {
2340 WriteChunk();
2341 }
2342 stream_->EndOfStream();
2343 }
2344
2345 private:
2346 template<typename T>
2347 void AddNumberImpl(T n, const char* format) {
2348 // Buffer for the longest value plus trailing \0
2349 static const int kMaxNumberSize =
2350 MaxDecimalDigitsIn<sizeof(T)>::kUnsigned + 1;
2351 if (chunk_size_ - chunk_pos_ >= kMaxNumberSize) {
2352 int result = OS::SNPrintF(
2353 chunk_.SubVector(chunk_pos_, chunk_size_), format, n);
2354 ASSERT(result != -1);
2355 chunk_pos_ += result;
2356 MaybeWriteChunk();
2357 } else {
2358 EmbeddedVector<char, kMaxNumberSize> buffer;
2359 int result = OS::SNPrintF(buffer, format, n);
2360 USE(result);
2361 ASSERT(result != -1);
2362 AddString(buffer.start());
2363 }
2364 }
2365 void MaybeWriteChunk() {
2366 ASSERT(chunk_pos_ <= chunk_size_);
2367 if (chunk_pos_ == chunk_size_) {
2368 WriteChunk();
2369 }
2370 }
2371 void WriteChunk() {
2372 if (aborted_) return;
2373 if (stream_->WriteAsciiChunk(chunk_.start(), chunk_pos_) ==
2374 v8::OutputStream::kAbort) aborted_ = true;
2375 chunk_pos_ = 0;
2376 }
2377
2378 v8::OutputStream* stream_;
2379 int chunk_size_;
2380 ScopedVector<char> chunk_;
2381 int chunk_pos_;
2382 bool aborted_;
2383};
2384
2385
2386// type, name|index, to_node.
2387const int HeapSnapshotJSONSerializer::kEdgeFieldsCount = 3;
2388// type, name, id, self_size, children_index.
2389const int HeapSnapshotJSONSerializer::kNodeFieldsCount = 5;
2390
2391void HeapSnapshotJSONSerializer::Serialize(v8::OutputStream* stream) {
2392 ASSERT(writer_ == NULL);
2393 writer_ = new OutputStreamWriter(stream);
ulan@chromium.org2e04b582013-02-21 14:06:02 +00002394 SerializeImpl();
ulan@chromium.org2e04b582013-02-21 14:06:02 +00002395 delete writer_;
2396 writer_ = NULL;
ulan@chromium.org2e04b582013-02-21 14:06:02 +00002397}
2398
2399
2400void HeapSnapshotJSONSerializer::SerializeImpl() {
2401 ASSERT(0 == snapshot_->root()->index());
2402 writer_->AddCharacter('{');
2403 writer_->AddString("\"snapshot\":{");
2404 SerializeSnapshot();
2405 if (writer_->aborted()) return;
2406 writer_->AddString("},\n");
2407 writer_->AddString("\"nodes\":[");
2408 SerializeNodes();
2409 if (writer_->aborted()) return;
2410 writer_->AddString("],\n");
2411 writer_->AddString("\"edges\":[");
2412 SerializeEdges();
2413 if (writer_->aborted()) return;
2414 writer_->AddString("],\n");
2415 writer_->AddString("\"strings\":[");
2416 SerializeStrings();
2417 if (writer_->aborted()) return;
2418 writer_->AddCharacter(']');
2419 writer_->AddCharacter('}');
2420 writer_->Finalize();
2421}
2422
2423
2424int HeapSnapshotJSONSerializer::GetStringId(const char* s) {
2425 HashMap::Entry* cache_entry = strings_.Lookup(
2426 const_cast<char*>(s), ObjectHash(s), true);
2427 if (cache_entry->value == NULL) {
2428 cache_entry->value = reinterpret_cast<void*>(next_string_id_++);
2429 }
2430 return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
2431}
2432
2433
2434static int utoa(unsigned value, const Vector<char>& buffer, int buffer_pos) {
2435 int number_of_digits = 0;
2436 unsigned t = value;
2437 do {
2438 ++number_of_digits;
2439 } while (t /= 10);
2440
2441 buffer_pos += number_of_digits;
2442 int result = buffer_pos;
2443 do {
2444 int last_digit = value % 10;
2445 buffer[--buffer_pos] = '0' + last_digit;
2446 value /= 10;
2447 } while (value);
2448 return result;
2449}
2450
2451
2452void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge,
2453 bool first_edge) {
2454 // The buffer needs space for 3 unsigned ints, 3 commas, \n and \0
2455 static const int kBufferSize =
2456 MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned * 3 + 3 + 2; // NOLINT
2457 EmbeddedVector<char, kBufferSize> buffer;
2458 int edge_name_or_index = edge->type() == HeapGraphEdge::kElement
2459 || edge->type() == HeapGraphEdge::kHidden
2460 || edge->type() == HeapGraphEdge::kWeak
2461 ? edge->index() : GetStringId(edge->name());
2462 int buffer_pos = 0;
2463 if (!first_edge) {
2464 buffer[buffer_pos++] = ',';
2465 }
2466 buffer_pos = utoa(edge->type(), buffer, buffer_pos);
2467 buffer[buffer_pos++] = ',';
2468 buffer_pos = utoa(edge_name_or_index, buffer, buffer_pos);
2469 buffer[buffer_pos++] = ',';
2470 buffer_pos = utoa(entry_index(edge->to()), buffer, buffer_pos);
2471 buffer[buffer_pos++] = '\n';
2472 buffer[buffer_pos++] = '\0';
2473 writer_->AddString(buffer.start());
2474}
2475
2476
2477void HeapSnapshotJSONSerializer::SerializeEdges() {
2478 List<HeapGraphEdge*>& edges = snapshot_->children();
2479 for (int i = 0; i < edges.length(); ++i) {
2480 ASSERT(i == 0 ||
2481 edges[i - 1]->from()->index() <= edges[i]->from()->index());
2482 SerializeEdge(edges[i], i == 0);
2483 if (writer_->aborted()) return;
2484 }
2485}
2486
2487
2488void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) {
2489 // The buffer needs space for 5 unsigned ints, 5 commas, \n and \0
2490 static const int kBufferSize =
2491 5 * MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned // NOLINT
2492 + 5 + 1 + 1;
2493 EmbeddedVector<char, kBufferSize> buffer;
2494 int buffer_pos = 0;
2495 if (entry_index(entry) != 0) {
2496 buffer[buffer_pos++] = ',';
2497 }
2498 buffer_pos = utoa(entry->type(), buffer, buffer_pos);
2499 buffer[buffer_pos++] = ',';
2500 buffer_pos = utoa(GetStringId(entry->name()), buffer, buffer_pos);
2501 buffer[buffer_pos++] = ',';
2502 buffer_pos = utoa(entry->id(), buffer, buffer_pos);
2503 buffer[buffer_pos++] = ',';
2504 buffer_pos = utoa(entry->self_size(), buffer, buffer_pos);
2505 buffer[buffer_pos++] = ',';
2506 buffer_pos = utoa(entry->children_count(), buffer, buffer_pos);
2507 buffer[buffer_pos++] = '\n';
2508 buffer[buffer_pos++] = '\0';
2509 writer_->AddString(buffer.start());
2510}
2511
2512
2513void HeapSnapshotJSONSerializer::SerializeNodes() {
2514 List<HeapEntry>& entries = snapshot_->entries();
2515 for (int i = 0; i < entries.length(); ++i) {
2516 SerializeNode(&entries[i]);
2517 if (writer_->aborted()) return;
2518 }
2519}
2520
2521
2522void HeapSnapshotJSONSerializer::SerializeSnapshot() {
2523 writer_->AddString("\"title\":\"");
2524 writer_->AddString(snapshot_->title());
2525 writer_->AddString("\"");
2526 writer_->AddString(",\"uid\":");
2527 writer_->AddNumber(snapshot_->uid());
2528 writer_->AddString(",\"meta\":");
2529 // The object describing node serialization layout.
2530 // We use a set of macros to improve readability.
2531#define JSON_A(s) "[" s "]"
2532#define JSON_O(s) "{" s "}"
2533#define JSON_S(s) "\"" s "\""
2534 writer_->AddString(JSON_O(
2535 JSON_S("node_fields") ":" JSON_A(
2536 JSON_S("type") ","
2537 JSON_S("name") ","
2538 JSON_S("id") ","
2539 JSON_S("self_size") ","
2540 JSON_S("edge_count")) ","
2541 JSON_S("node_types") ":" JSON_A(
2542 JSON_A(
2543 JSON_S("hidden") ","
2544 JSON_S("array") ","
2545 JSON_S("string") ","
2546 JSON_S("object") ","
2547 JSON_S("code") ","
2548 JSON_S("closure") ","
2549 JSON_S("regexp") ","
2550 JSON_S("number") ","
2551 JSON_S("native") ","
svenpanne@chromium.org876cca82013-03-18 14:43:20 +00002552 JSON_S("synthetic")) ","
ulan@chromium.org2e04b582013-02-21 14:06:02 +00002553 JSON_S("string") ","
2554 JSON_S("number") ","
2555 JSON_S("number") ","
2556 JSON_S("number") ","
2557 JSON_S("number") ","
2558 JSON_S("number")) ","
2559 JSON_S("edge_fields") ":" JSON_A(
2560 JSON_S("type") ","
2561 JSON_S("name_or_index") ","
2562 JSON_S("to_node")) ","
2563 JSON_S("edge_types") ":" JSON_A(
2564 JSON_A(
2565 JSON_S("context") ","
2566 JSON_S("element") ","
2567 JSON_S("property") ","
2568 JSON_S("internal") ","
2569 JSON_S("hidden") ","
2570 JSON_S("shortcut") ","
2571 JSON_S("weak")) ","
2572 JSON_S("string_or_number") ","
2573 JSON_S("node"))));
2574#undef JSON_S
2575#undef JSON_O
2576#undef JSON_A
2577 writer_->AddString(",\"node_count\":");
2578 writer_->AddNumber(snapshot_->entries().length());
2579 writer_->AddString(",\"edge_count\":");
2580 writer_->AddNumber(snapshot_->edges().length());
2581}
2582
2583
2584static void WriteUChar(OutputStreamWriter* w, unibrow::uchar u) {
2585 static const char hex_chars[] = "0123456789ABCDEF";
2586 w->AddString("\\u");
2587 w->AddCharacter(hex_chars[(u >> 12) & 0xf]);
2588 w->AddCharacter(hex_chars[(u >> 8) & 0xf]);
2589 w->AddCharacter(hex_chars[(u >> 4) & 0xf]);
2590 w->AddCharacter(hex_chars[u & 0xf]);
2591}
2592
2593void HeapSnapshotJSONSerializer::SerializeString(const unsigned char* s) {
2594 writer_->AddCharacter('\n');
2595 writer_->AddCharacter('\"');
2596 for ( ; *s != '\0'; ++s) {
2597 switch (*s) {
2598 case '\b':
2599 writer_->AddString("\\b");
2600 continue;
2601 case '\f':
2602 writer_->AddString("\\f");
2603 continue;
2604 case '\n':
2605 writer_->AddString("\\n");
2606 continue;
2607 case '\r':
2608 writer_->AddString("\\r");
2609 continue;
2610 case '\t':
2611 writer_->AddString("\\t");
2612 continue;
2613 case '\"':
2614 case '\\':
2615 writer_->AddCharacter('\\');
2616 writer_->AddCharacter(*s);
2617 continue;
2618 default:
2619 if (*s > 31 && *s < 128) {
2620 writer_->AddCharacter(*s);
2621 } else if (*s <= 31) {
2622 // Special character with no dedicated literal.
2623 WriteUChar(writer_, *s);
2624 } else {
2625 // Convert UTF-8 into \u UTF-16 literal.
2626 unsigned length = 1, cursor = 0;
2627 for ( ; length <= 4 && *(s + length) != '\0'; ++length) { }
2628 unibrow::uchar c = unibrow::Utf8::CalculateValue(s, length, &cursor);
2629 if (c != unibrow::Utf8::kBadChar) {
2630 WriteUChar(writer_, c);
2631 ASSERT(cursor != 0);
2632 s += cursor - 1;
2633 } else {
2634 writer_->AddCharacter('?');
2635 }
2636 }
2637 }
2638 }
2639 writer_->AddCharacter('\"');
2640}
2641
2642
2643void HeapSnapshotJSONSerializer::SerializeStrings() {
2644 List<HashMap::Entry*> sorted_strings;
2645 SortHashMap(&strings_, &sorted_strings);
2646 writer_->AddString("\"<dummy>\"");
2647 for (int i = 0; i < sorted_strings.length(); ++i) {
2648 writer_->AddCharacter(',');
2649 SerializeString(
2650 reinterpret_cast<const unsigned char*>(sorted_strings[i]->key));
2651 if (writer_->aborted()) return;
2652 }
2653}
2654
2655
2656template<typename T>
2657inline static int SortUsingEntryValue(const T* x, const T* y) {
2658 uintptr_t x_uint = reinterpret_cast<uintptr_t>((*x)->value);
2659 uintptr_t y_uint = reinterpret_cast<uintptr_t>((*y)->value);
2660 if (x_uint > y_uint) {
2661 return 1;
2662 } else if (x_uint == y_uint) {
2663 return 0;
2664 } else {
2665 return -1;
2666 }
2667}
2668
2669
2670void HeapSnapshotJSONSerializer::SortHashMap(
2671 HashMap* map, List<HashMap::Entry*>* sorted_entries) {
2672 for (HashMap::Entry* p = map->Start(); p != NULL; p = map->Next(p))
2673 sorted_entries->Add(p);
2674 sorted_entries->Sort(SortUsingEntryValue);
2675}
2676
2677} } // namespace v8::internal