1 // Copyright 2012 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 "profile-generator-inl.h"
31
32 #include "global-handles.h"
33 #include "heap-profiler.h"
34 #include "scopeinfo.h"
35 #include "unicode.h"
36 #include "zone-inl.h"
37
38 namespace v8 {
39 namespace internal {
40
41
TokenEnumerator()42 TokenEnumerator::TokenEnumerator()
43 : token_locations_(4),
44 token_removed_(4) {
45 }
46
47
~TokenEnumerator()48 TokenEnumerator::~TokenEnumerator() {
49 Isolate* isolate = Isolate::Current();
50 for (int i = 0; i < token_locations_.length(); ++i) {
51 if (!token_removed_[i]) {
52 isolate->global_handles()->ClearWeakness(token_locations_[i]);
53 isolate->global_handles()->Destroy(token_locations_[i]);
54 }
55 }
56 }
57
58
GetTokenId(Object * token)59 int TokenEnumerator::GetTokenId(Object* token) {
60 Isolate* isolate = Isolate::Current();
61 if (token == NULL) return TokenEnumerator::kNoSecurityToken;
62 for (int i = 0; i < token_locations_.length(); ++i) {
63 if (*token_locations_[i] == token && !token_removed_[i]) return i;
64 }
65 Handle<Object> handle = isolate->global_handles()->Create(token);
66 // handle.location() points to a memory cell holding a pointer
67 // to a token object in the V8's heap.
68 isolate->global_handles()->MakeWeak(handle.location(), this,
69 TokenRemovedCallback);
70 token_locations_.Add(handle.location());
71 token_removed_.Add(false);
72 return token_locations_.length() - 1;
73 }
74
75
TokenRemovedCallback(v8::Persistent<v8::Value> handle,void * parameter)76 void TokenEnumerator::TokenRemovedCallback(v8::Persistent<v8::Value> handle,
77 void* parameter) {
78 reinterpret_cast<TokenEnumerator*>(parameter)->TokenRemoved(
79 Utils::OpenHandle(*handle).location());
80 handle.Dispose();
81 }
82
83
TokenRemoved(Object ** token_location)84 void TokenEnumerator::TokenRemoved(Object** token_location) {
85 for (int i = 0; i < token_locations_.length(); ++i) {
86 if (token_locations_[i] == token_location && !token_removed_[i]) {
87 token_removed_[i] = true;
88 return;
89 }
90 }
91 }
92
93
StringsStorage()94 StringsStorage::StringsStorage()
95 : names_(StringsMatch) {
96 }
97
98
~StringsStorage()99 StringsStorage::~StringsStorage() {
100 for (HashMap::Entry* p = names_.Start();
101 p != NULL;
102 p = names_.Next(p)) {
103 DeleteArray(reinterpret_cast<const char*>(p->value));
104 }
105 }
106
107
GetCopy(const char * src)108 const char* StringsStorage::GetCopy(const char* src) {
109 int len = static_cast<int>(strlen(src));
110 Vector<char> dst = Vector<char>::New(len + 1);
111 OS::StrNCpy(dst, src, len);
112 dst[len] = '\0';
113 uint32_t hash =
114 HashSequentialString(dst.start(), len, HEAP->HashSeed());
115 return AddOrDisposeString(dst.start(), hash);
116 }
117
118
GetFormatted(const char * format,...)119 const char* StringsStorage::GetFormatted(const char* format, ...) {
120 va_list args;
121 va_start(args, format);
122 const char* result = GetVFormatted(format, args);
123 va_end(args);
124 return result;
125 }
126
127
AddOrDisposeString(char * str,uint32_t hash)128 const char* StringsStorage::AddOrDisposeString(char* str, uint32_t hash) {
129 HashMap::Entry* cache_entry = names_.Lookup(str, hash, true);
130 if (cache_entry->value == NULL) {
131 // New entry added.
132 cache_entry->value = str;
133 } else {
134 DeleteArray(str);
135 }
136 return reinterpret_cast<const char*>(cache_entry->value);
137 }
138
139
GetVFormatted(const char * format,va_list args)140 const char* StringsStorage::GetVFormatted(const char* format, va_list args) {
141 Vector<char> str = Vector<char>::New(1024);
142 int len = OS::VSNPrintF(str, format, args);
143 if (len == -1) {
144 DeleteArray(str.start());
145 return format;
146 }
147 uint32_t hash = HashSequentialString(
148 str.start(), len, HEAP->HashSeed());
149 return AddOrDisposeString(str.start(), hash);
150 }
151
152
GetName(String * name)153 const char* StringsStorage::GetName(String* name) {
154 if (name->IsString()) {
155 int length = Min(kMaxNameSize, name->length());
156 SmartArrayPointer<char> data =
157 name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL, 0, length);
158 uint32_t hash =
159 HashSequentialString(*data, length, name->GetHeap()->HashSeed());
160 return AddOrDisposeString(data.Detach(), hash);
161 }
162 return "";
163 }
164
165
GetName(int index)166 const char* StringsStorage::GetName(int index) {
167 return GetFormatted("%d", index);
168 }
169
170
171 const char* const CodeEntry::kEmptyNamePrefix = "";
172
173
CopyData(const CodeEntry & source)174 void CodeEntry::CopyData(const CodeEntry& source) {
175 tag_ = source.tag_;
176 name_prefix_ = source.name_prefix_;
177 name_ = source.name_;
178 resource_name_ = source.resource_name_;
179 line_number_ = source.line_number_;
180 }
181
182
GetCallUid() const183 uint32_t CodeEntry::GetCallUid() const {
184 uint32_t hash = ComputeIntegerHash(tag_, v8::internal::kZeroHashSeed);
185 if (shared_id_ != 0) {
186 hash ^= ComputeIntegerHash(static_cast<uint32_t>(shared_id_),
187 v8::internal::kZeroHashSeed);
188 } else {
189 hash ^= ComputeIntegerHash(
190 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_prefix_)),
191 v8::internal::kZeroHashSeed);
192 hash ^= ComputeIntegerHash(
193 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_)),
194 v8::internal::kZeroHashSeed);
195 hash ^= ComputeIntegerHash(
196 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(resource_name_)),
197 v8::internal::kZeroHashSeed);
198 hash ^= ComputeIntegerHash(line_number_, v8::internal::kZeroHashSeed);
199 }
200 return hash;
201 }
202
203
IsSameAs(CodeEntry * entry) const204 bool CodeEntry::IsSameAs(CodeEntry* entry) const {
205 return this == entry
206 || (tag_ == entry->tag_
207 && shared_id_ == entry->shared_id_
208 && (shared_id_ != 0
209 || (name_prefix_ == entry->name_prefix_
210 && name_ == entry->name_
211 && resource_name_ == entry->resource_name_
212 && line_number_ == entry->line_number_)));
213 }
214
215
FindChild(CodeEntry * entry)216 ProfileNode* ProfileNode::FindChild(CodeEntry* entry) {
217 HashMap::Entry* map_entry =
218 children_.Lookup(entry, CodeEntryHash(entry), false);
219 return map_entry != NULL ?
220 reinterpret_cast<ProfileNode*>(map_entry->value) : NULL;
221 }
222
223
FindOrAddChild(CodeEntry * entry)224 ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) {
225 HashMap::Entry* map_entry =
226 children_.Lookup(entry, CodeEntryHash(entry), true);
227 if (map_entry->value == NULL) {
228 // New node added.
229 ProfileNode* new_node = new ProfileNode(tree_, entry);
230 map_entry->value = new_node;
231 children_list_.Add(new_node);
232 }
233 return reinterpret_cast<ProfileNode*>(map_entry->value);
234 }
235
236
GetSelfMillis() const237 double ProfileNode::GetSelfMillis() const {
238 return tree_->TicksToMillis(self_ticks_);
239 }
240
241
GetTotalMillis() const242 double ProfileNode::GetTotalMillis() const {
243 return tree_->TicksToMillis(total_ticks_);
244 }
245
246
Print(int indent)247 void ProfileNode::Print(int indent) {
248 OS::Print("%5u %5u %*c %s%s [%d]",
249 total_ticks_, self_ticks_,
250 indent, ' ',
251 entry_->name_prefix(),
252 entry_->name(),
253 entry_->security_token_id());
254 if (entry_->resource_name()[0] != '\0')
255 OS::Print(" %s:%d", entry_->resource_name(), entry_->line_number());
256 OS::Print("\n");
257 for (HashMap::Entry* p = children_.Start();
258 p != NULL;
259 p = children_.Next(p)) {
260 reinterpret_cast<ProfileNode*>(p->value)->Print(indent + 2);
261 }
262 }
263
264
265 class DeleteNodesCallback {
266 public:
BeforeTraversingChild(ProfileNode *,ProfileNode *)267 void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }
268
AfterAllChildrenTraversed(ProfileNode * node)269 void AfterAllChildrenTraversed(ProfileNode* node) {
270 delete node;
271 }
272
AfterChildTraversed(ProfileNode *,ProfileNode *)273 void AfterChildTraversed(ProfileNode*, ProfileNode*) { }
274 };
275
276
ProfileTree()277 ProfileTree::ProfileTree()
278 : root_entry_(Logger::FUNCTION_TAG,
279 "",
280 "(root)",
281 "",
282 0,
283 TokenEnumerator::kNoSecurityToken),
284 root_(new ProfileNode(this, &root_entry_)) {
285 }
286
287
~ProfileTree()288 ProfileTree::~ProfileTree() {
289 DeleteNodesCallback cb;
290 TraverseDepthFirst(&cb);
291 }
292
293
AddPathFromEnd(const Vector<CodeEntry * > & path)294 void ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path) {
295 ProfileNode* node = root_;
296 for (CodeEntry** entry = path.start() + path.length() - 1;
297 entry != path.start() - 1;
298 --entry) {
299 if (*entry != NULL) {
300 node = node->FindOrAddChild(*entry);
301 }
302 }
303 node->IncrementSelfTicks();
304 }
305
306
AddPathFromStart(const Vector<CodeEntry * > & path)307 void ProfileTree::AddPathFromStart(const Vector<CodeEntry*>& path) {
308 ProfileNode* node = root_;
309 for (CodeEntry** entry = path.start();
310 entry != path.start() + path.length();
311 ++entry) {
312 if (*entry != NULL) {
313 node = node->FindOrAddChild(*entry);
314 }
315 }
316 node->IncrementSelfTicks();
317 }
318
319
320 struct NodesPair {
NodesPairv8::internal::NodesPair321 NodesPair(ProfileNode* src, ProfileNode* dst)
322 : src(src), dst(dst) { }
323 ProfileNode* src;
324 ProfileNode* dst;
325 };
326
327
328 class FilteredCloneCallback {
329 public:
FilteredCloneCallback(ProfileNode * dst_root,int security_token_id)330 FilteredCloneCallback(ProfileNode* dst_root, int security_token_id)
331 : stack_(10),
332 security_token_id_(security_token_id) {
333 stack_.Add(NodesPair(NULL, dst_root));
334 }
335
BeforeTraversingChild(ProfileNode * parent,ProfileNode * child)336 void BeforeTraversingChild(ProfileNode* parent, ProfileNode* child) {
337 if (IsTokenAcceptable(child->entry()->security_token_id(),
338 parent->entry()->security_token_id())) {
339 ProfileNode* clone = stack_.last().dst->FindOrAddChild(child->entry());
340 clone->IncreaseSelfTicks(child->self_ticks());
341 stack_.Add(NodesPair(child, clone));
342 } else {
343 // Attribute ticks to parent node.
344 stack_.last().dst->IncreaseSelfTicks(child->self_ticks());
345 }
346 }
347
AfterAllChildrenTraversed(ProfileNode * parent)348 void AfterAllChildrenTraversed(ProfileNode* parent) { }
349
AfterChildTraversed(ProfileNode *,ProfileNode * child)350 void AfterChildTraversed(ProfileNode*, ProfileNode* child) {
351 if (stack_.last().src == child) {
352 stack_.RemoveLast();
353 }
354 }
355
356 private:
IsTokenAcceptable(int token,int parent_token)357 bool IsTokenAcceptable(int token, int parent_token) {
358 if (token == TokenEnumerator::kNoSecurityToken
359 || token == security_token_id_) return true;
360 if (token == TokenEnumerator::kInheritsSecurityToken) {
361 ASSERT(parent_token != TokenEnumerator::kInheritsSecurityToken);
362 return parent_token == TokenEnumerator::kNoSecurityToken
363 || parent_token == security_token_id_;
364 }
365 return false;
366 }
367
368 List<NodesPair> stack_;
369 int security_token_id_;
370 };
371
FilteredClone(ProfileTree * src,int security_token_id)372 void ProfileTree::FilteredClone(ProfileTree* src, int security_token_id) {
373 ms_to_ticks_scale_ = src->ms_to_ticks_scale_;
374 FilteredCloneCallback cb(root_, security_token_id);
375 src->TraverseDepthFirst(&cb);
376 CalculateTotalTicks();
377 }
378
379
SetTickRatePerMs(double ticks_per_ms)380 void ProfileTree::SetTickRatePerMs(double ticks_per_ms) {
381 ms_to_ticks_scale_ = ticks_per_ms > 0 ? 1.0 / ticks_per_ms : 1.0;
382 }
383
384
385 class Position {
386 public:
Position(ProfileNode * node)387 explicit Position(ProfileNode* node)
388 : node(node), child_idx_(0) { }
INLINE(ProfileNode * current_child ())389 INLINE(ProfileNode* current_child()) {
390 return node->children()->at(child_idx_);
391 }
INLINE(bool has_current_child ())392 INLINE(bool has_current_child()) {
393 return child_idx_ < node->children()->length();
394 }
INLINE(void next_child ())395 INLINE(void next_child()) { ++child_idx_; }
396
397 ProfileNode* node;
398 private:
399 int child_idx_;
400 };
401
402
403 // Non-recursive implementation of a depth-first post-order tree traversal.
404 template <typename Callback>
TraverseDepthFirst(Callback * callback)405 void ProfileTree::TraverseDepthFirst(Callback* callback) {
406 List<Position> stack(10);
407 stack.Add(Position(root_));
408 while (stack.length() > 0) {
409 Position& current = stack.last();
410 if (current.has_current_child()) {
411 callback->BeforeTraversingChild(current.node, current.current_child());
412 stack.Add(Position(current.current_child()));
413 } else {
414 callback->AfterAllChildrenTraversed(current.node);
415 if (stack.length() > 1) {
416 Position& parent = stack[stack.length() - 2];
417 callback->AfterChildTraversed(parent.node, current.node);
418 parent.next_child();
419 }
420 // Remove child from the stack.
421 stack.RemoveLast();
422 }
423 }
424 }
425
426
427 class CalculateTotalTicksCallback {
428 public:
BeforeTraversingChild(ProfileNode *,ProfileNode *)429 void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }
430
AfterAllChildrenTraversed(ProfileNode * node)431 void AfterAllChildrenTraversed(ProfileNode* node) {
432 node->IncreaseTotalTicks(node->self_ticks());
433 }
434
AfterChildTraversed(ProfileNode * parent,ProfileNode * child)435 void AfterChildTraversed(ProfileNode* parent, ProfileNode* child) {
436 parent->IncreaseTotalTicks(child->total_ticks());
437 }
438 };
439
440
CalculateTotalTicks()441 void ProfileTree::CalculateTotalTicks() {
442 CalculateTotalTicksCallback cb;
443 TraverseDepthFirst(&cb);
444 }
445
446
ShortPrint()447 void ProfileTree::ShortPrint() {
448 OS::Print("root: %u %u %.2fms %.2fms\n",
449 root_->total_ticks(), root_->self_ticks(),
450 root_->GetTotalMillis(), root_->GetSelfMillis());
451 }
452
453
AddPath(const Vector<CodeEntry * > & path)454 void CpuProfile::AddPath(const Vector<CodeEntry*>& path) {
455 top_down_.AddPathFromEnd(path);
456 bottom_up_.AddPathFromStart(path);
457 }
458
459
CalculateTotalTicks()460 void CpuProfile::CalculateTotalTicks() {
461 top_down_.CalculateTotalTicks();
462 bottom_up_.CalculateTotalTicks();
463 }
464
465
SetActualSamplingRate(double actual_sampling_rate)466 void CpuProfile::SetActualSamplingRate(double actual_sampling_rate) {
467 top_down_.SetTickRatePerMs(actual_sampling_rate);
468 bottom_up_.SetTickRatePerMs(actual_sampling_rate);
469 }
470
471
FilteredClone(int security_token_id)472 CpuProfile* CpuProfile::FilteredClone(int security_token_id) {
473 ASSERT(security_token_id != TokenEnumerator::kNoSecurityToken);
474 CpuProfile* clone = new CpuProfile(title_, uid_);
475 clone->top_down_.FilteredClone(&top_down_, security_token_id);
476 clone->bottom_up_.FilteredClone(&bottom_up_, security_token_id);
477 return clone;
478 }
479
480
ShortPrint()481 void CpuProfile::ShortPrint() {
482 OS::Print("top down ");
483 top_down_.ShortPrint();
484 OS::Print("bottom up ");
485 bottom_up_.ShortPrint();
486 }
487
488
Print()489 void CpuProfile::Print() {
490 OS::Print("[Top down]:\n");
491 top_down_.Print();
492 OS::Print("[Bottom up]:\n");
493 bottom_up_.Print();
494 }
495
496
497 CodeEntry* const CodeMap::kSharedFunctionCodeEntry = NULL;
498 const CodeMap::CodeTreeConfig::Key CodeMap::CodeTreeConfig::kNoKey = NULL;
499
500
AddCode(Address addr,CodeEntry * entry,unsigned size)501 void CodeMap::AddCode(Address addr, CodeEntry* entry, unsigned size) {
502 DeleteAllCoveredCode(addr, addr + size);
503 CodeTree::Locator locator;
504 tree_.Insert(addr, &locator);
505 locator.set_value(CodeEntryInfo(entry, size));
506 }
507
508
DeleteAllCoveredCode(Address start,Address end)509 void CodeMap::DeleteAllCoveredCode(Address start, Address end) {
510 List<Address> to_delete;
511 Address addr = end - 1;
512 while (addr >= start) {
513 CodeTree::Locator locator;
514 if (!tree_.FindGreatestLessThan(addr, &locator)) break;
515 Address start2 = locator.key(), end2 = start2 + locator.value().size;
516 if (start2 < end && start < end2) to_delete.Add(start2);
517 addr = start2 - 1;
518 }
519 for (int i = 0; i < to_delete.length(); ++i) tree_.Remove(to_delete[i]);
520 }
521
522
FindEntry(Address addr)523 CodeEntry* CodeMap::FindEntry(Address addr) {
524 CodeTree::Locator locator;
525 if (tree_.FindGreatestLessThan(addr, &locator)) {
526 // locator.key() <= addr. Need to check that addr is within entry.
527 const CodeEntryInfo& entry = locator.value();
528 if (addr < (locator.key() + entry.size))
529 return entry.entry;
530 }
531 return NULL;
532 }
533
534
GetSharedId(Address addr)535 int CodeMap::GetSharedId(Address addr) {
536 CodeTree::Locator locator;
537 // For shared function entries, 'size' field is used to store their IDs.
538 if (tree_.Find(addr, &locator)) {
539 const CodeEntryInfo& entry = locator.value();
540 ASSERT(entry.entry == kSharedFunctionCodeEntry);
541 return entry.size;
542 } else {
543 tree_.Insert(addr, &locator);
544 int id = next_shared_id_++;
545 locator.set_value(CodeEntryInfo(kSharedFunctionCodeEntry, id));
546 return id;
547 }
548 }
549
550
MoveCode(Address from,Address to)551 void CodeMap::MoveCode(Address from, Address to) {
552 if (from == to) return;
553 CodeTree::Locator locator;
554 if (!tree_.Find(from, &locator)) return;
555 CodeEntryInfo entry = locator.value();
556 tree_.Remove(from);
557 AddCode(to, entry.entry, entry.size);
558 }
559
560
Call(const Address & key,const CodeMap::CodeEntryInfo & value)561 void CodeMap::CodeTreePrinter::Call(
562 const Address& key, const CodeMap::CodeEntryInfo& value) {
563 OS::Print("%p %5d %s\n", key, value.size, value.entry->name());
564 }
565
566
Print()567 void CodeMap::Print() {
568 CodeTreePrinter printer;
569 tree_.ForEach(&printer);
570 }
571
572
CpuProfilesCollection()573 CpuProfilesCollection::CpuProfilesCollection()
574 : profiles_uids_(UidsMatch),
575 current_profiles_semaphore_(OS::CreateSemaphore(1)) {
576 // Create list of unabridged profiles.
577 profiles_by_token_.Add(new List<CpuProfile*>());
578 }
579
580
DeleteCodeEntry(CodeEntry ** entry_ptr)581 static void DeleteCodeEntry(CodeEntry** entry_ptr) {
582 delete *entry_ptr;
583 }
584
DeleteCpuProfile(CpuProfile ** profile_ptr)585 static void DeleteCpuProfile(CpuProfile** profile_ptr) {
586 delete *profile_ptr;
587 }
588
DeleteProfilesList(List<CpuProfile * > ** list_ptr)589 static void DeleteProfilesList(List<CpuProfile*>** list_ptr) {
590 if (*list_ptr != NULL) {
591 (*list_ptr)->Iterate(DeleteCpuProfile);
592 delete *list_ptr;
593 }
594 }
595
~CpuProfilesCollection()596 CpuProfilesCollection::~CpuProfilesCollection() {
597 delete current_profiles_semaphore_;
598 current_profiles_.Iterate(DeleteCpuProfile);
599 detached_profiles_.Iterate(DeleteCpuProfile);
600 profiles_by_token_.Iterate(DeleteProfilesList);
601 code_entries_.Iterate(DeleteCodeEntry);
602 }
603
604
StartProfiling(const char * title,unsigned uid)605 bool CpuProfilesCollection::StartProfiling(const char* title, unsigned uid) {
606 ASSERT(uid > 0);
607 current_profiles_semaphore_->Wait();
608 if (current_profiles_.length() >= kMaxSimultaneousProfiles) {
609 current_profiles_semaphore_->Signal();
610 return false;
611 }
612 for (int i = 0; i < current_profiles_.length(); ++i) {
613 if (strcmp(current_profiles_[i]->title(), title) == 0) {
614 // Ignore attempts to start profile with the same title.
615 current_profiles_semaphore_->Signal();
616 return false;
617 }
618 }
619 current_profiles_.Add(new CpuProfile(title, uid));
620 current_profiles_semaphore_->Signal();
621 return true;
622 }
623
624
StartProfiling(String * title,unsigned uid)625 bool CpuProfilesCollection::StartProfiling(String* title, unsigned uid) {
626 return StartProfiling(GetName(title), uid);
627 }
628
629
StopProfiling(int security_token_id,const char * title,double actual_sampling_rate)630 CpuProfile* CpuProfilesCollection::StopProfiling(int security_token_id,
631 const char* title,
632 double actual_sampling_rate) {
633 const int title_len = StrLength(title);
634 CpuProfile* profile = NULL;
635 current_profiles_semaphore_->Wait();
636 for (int i = current_profiles_.length() - 1; i >= 0; --i) {
637 if (title_len == 0 || strcmp(current_profiles_[i]->title(), title) == 0) {
638 profile = current_profiles_.Remove(i);
639 break;
640 }
641 }
642 current_profiles_semaphore_->Signal();
643
644 if (profile != NULL) {
645 profile->CalculateTotalTicks();
646 profile->SetActualSamplingRate(actual_sampling_rate);
647 List<CpuProfile*>* unabridged_list =
648 profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
649 unabridged_list->Add(profile);
650 HashMap::Entry* entry =
651 profiles_uids_.Lookup(reinterpret_cast<void*>(profile->uid()),
652 static_cast<uint32_t>(profile->uid()),
653 true);
654 ASSERT(entry->value == NULL);
655 entry->value = reinterpret_cast<void*>(unabridged_list->length() - 1);
656 return GetProfile(security_token_id, profile->uid());
657 }
658 return NULL;
659 }
660
661
GetProfile(int security_token_id,unsigned uid)662 CpuProfile* CpuProfilesCollection::GetProfile(int security_token_id,
663 unsigned uid) {
664 int index = GetProfileIndex(uid);
665 if (index < 0) return NULL;
666 List<CpuProfile*>* unabridged_list =
667 profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
668 if (security_token_id == TokenEnumerator::kNoSecurityToken) {
669 return unabridged_list->at(index);
670 }
671 List<CpuProfile*>* list = GetProfilesList(security_token_id);
672 if (list->at(index) == NULL) {
673 (*list)[index] =
674 unabridged_list->at(index)->FilteredClone(security_token_id);
675 }
676 return list->at(index);
677 }
678
679
GetProfileIndex(unsigned uid)680 int CpuProfilesCollection::GetProfileIndex(unsigned uid) {
681 HashMap::Entry* entry = profiles_uids_.Lookup(reinterpret_cast<void*>(uid),
682 static_cast<uint32_t>(uid),
683 false);
684 return entry != NULL ?
685 static_cast<int>(reinterpret_cast<intptr_t>(entry->value)) : -1;
686 }
687
688
IsLastProfile(const char * title)689 bool CpuProfilesCollection::IsLastProfile(const char* title) {
690 // Called from VM thread, and only it can mutate the list,
691 // so no locking is needed here.
692 if (current_profiles_.length() != 1) return false;
693 return StrLength(title) == 0
694 || strcmp(current_profiles_[0]->title(), title) == 0;
695 }
696
697
RemoveProfile(CpuProfile * profile)698 void CpuProfilesCollection::RemoveProfile(CpuProfile* profile) {
699 // Called from VM thread for a completed profile.
700 unsigned uid = profile->uid();
701 int index = GetProfileIndex(uid);
702 if (index < 0) {
703 detached_profiles_.RemoveElement(profile);
704 return;
705 }
706 profiles_uids_.Remove(reinterpret_cast<void*>(uid),
707 static_cast<uint32_t>(uid));
708 // Decrement all indexes above the deleted one.
709 for (HashMap::Entry* p = profiles_uids_.Start();
710 p != NULL;
711 p = profiles_uids_.Next(p)) {
712 intptr_t p_index = reinterpret_cast<intptr_t>(p->value);
713 if (p_index > index) {
714 p->value = reinterpret_cast<void*>(p_index - 1);
715 }
716 }
717 for (int i = 0; i < profiles_by_token_.length(); ++i) {
718 List<CpuProfile*>* list = profiles_by_token_[i];
719 if (list != NULL && index < list->length()) {
720 // Move all filtered clones into detached_profiles_,
721 // so we can know that they are still in use.
722 CpuProfile* cloned_profile = list->Remove(index);
723 if (cloned_profile != NULL && cloned_profile != profile) {
724 detached_profiles_.Add(cloned_profile);
725 }
726 }
727 }
728 }
729
730
TokenToIndex(int security_token_id)731 int CpuProfilesCollection::TokenToIndex(int security_token_id) {
732 ASSERT(TokenEnumerator::kNoSecurityToken == -1);
733 return security_token_id + 1; // kNoSecurityToken -> 0, 0 -> 1, ...
734 }
735
736
GetProfilesList(int security_token_id)737 List<CpuProfile*>* CpuProfilesCollection::GetProfilesList(
738 int security_token_id) {
739 const int index = TokenToIndex(security_token_id);
740 const int lists_to_add = index - profiles_by_token_.length() + 1;
741 if (lists_to_add > 0) profiles_by_token_.AddBlock(NULL, lists_to_add);
742 List<CpuProfile*>* unabridged_list =
743 profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
744 const int current_count = unabridged_list->length();
745 if (profiles_by_token_[index] == NULL) {
746 profiles_by_token_[index] = new List<CpuProfile*>(current_count);
747 }
748 List<CpuProfile*>* list = profiles_by_token_[index];
749 const int profiles_to_add = current_count - list->length();
750 if (profiles_to_add > 0) list->AddBlock(NULL, profiles_to_add);
751 return list;
752 }
753
754
Profiles(int security_token_id)755 List<CpuProfile*>* CpuProfilesCollection::Profiles(int security_token_id) {
756 List<CpuProfile*>* unabridged_list =
757 profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
758 if (security_token_id == TokenEnumerator::kNoSecurityToken) {
759 return unabridged_list;
760 }
761 List<CpuProfile*>* list = GetProfilesList(security_token_id);
762 const int current_count = unabridged_list->length();
763 for (int i = 0; i < current_count; ++i) {
764 if (list->at(i) == NULL) {
765 (*list)[i] = unabridged_list->at(i)->FilteredClone(security_token_id);
766 }
767 }
768 return list;
769 }
770
771
NewCodeEntry(Logger::LogEventsAndTags tag,String * name,String * resource_name,int line_number)772 CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
773 String* name,
774 String* resource_name,
775 int line_number) {
776 CodeEntry* entry = new CodeEntry(tag,
777 CodeEntry::kEmptyNamePrefix,
778 GetFunctionName(name),
779 GetName(resource_name),
780 line_number,
781 TokenEnumerator::kNoSecurityToken);
782 code_entries_.Add(entry);
783 return entry;
784 }
785
786
NewCodeEntry(Logger::LogEventsAndTags tag,const char * name)787 CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
788 const char* name) {
789 CodeEntry* entry = new CodeEntry(tag,
790 CodeEntry::kEmptyNamePrefix,
791 GetFunctionName(name),
792 "",
793 v8::CpuProfileNode::kNoLineNumberInfo,
794 TokenEnumerator::kNoSecurityToken);
795 code_entries_.Add(entry);
796 return entry;
797 }
798
799
NewCodeEntry(Logger::LogEventsAndTags tag,const char * name_prefix,String * name)800 CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
801 const char* name_prefix,
802 String* name) {
803 CodeEntry* entry = new CodeEntry(tag,
804 name_prefix,
805 GetName(name),
806 "",
807 v8::CpuProfileNode::kNoLineNumberInfo,
808 TokenEnumerator::kInheritsSecurityToken);
809 code_entries_.Add(entry);
810 return entry;
811 }
812
813
NewCodeEntry(Logger::LogEventsAndTags tag,int args_count)814 CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
815 int args_count) {
816 CodeEntry* entry = new CodeEntry(tag,
817 "args_count: ",
818 GetName(args_count),
819 "",
820 v8::CpuProfileNode::kNoLineNumberInfo,
821 TokenEnumerator::kInheritsSecurityToken);
822 code_entries_.Add(entry);
823 return entry;
824 }
825
826
AddPathToCurrentProfiles(const Vector<CodeEntry * > & path)827 void CpuProfilesCollection::AddPathToCurrentProfiles(
828 const Vector<CodeEntry*>& path) {
829 // As starting / stopping profiles is rare relatively to this
830 // method, we don't bother minimizing the duration of lock holding,
831 // e.g. copying contents of the list to a local vector.
832 current_profiles_semaphore_->Wait();
833 for (int i = 0; i < current_profiles_.length(); ++i) {
834 current_profiles_[i]->AddPath(path);
835 }
836 current_profiles_semaphore_->Signal();
837 }
838
839
Tick()840 void SampleRateCalculator::Tick() {
841 if (--wall_time_query_countdown_ == 0)
842 UpdateMeasurements(OS::TimeCurrentMillis());
843 }
844
845
UpdateMeasurements(double current_time)846 void SampleRateCalculator::UpdateMeasurements(double current_time) {
847 if (measurements_count_++ != 0) {
848 const double measured_ticks_per_ms =
849 (kWallTimeQueryIntervalMs * ticks_per_ms_) /
850 (current_time - last_wall_time_);
851 // Update the average value.
852 ticks_per_ms_ +=
853 (measured_ticks_per_ms - ticks_per_ms_) / measurements_count_;
854 // Update the externally accessible result.
855 result_ = static_cast<AtomicWord>(ticks_per_ms_ * kResultScale);
856 }
857 last_wall_time_ = current_time;
858 wall_time_query_countdown_ =
859 static_cast<unsigned>(kWallTimeQueryIntervalMs * ticks_per_ms_);
860 }
861
862
863 const char* const ProfileGenerator::kAnonymousFunctionName =
864 "(anonymous function)";
865 const char* const ProfileGenerator::kProgramEntryName =
866 "(program)";
867 const char* const ProfileGenerator::kGarbageCollectorEntryName =
868 "(garbage collector)";
869
870
ProfileGenerator(CpuProfilesCollection * profiles)871 ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles)
872 : profiles_(profiles),
873 program_entry_(
874 profiles->NewCodeEntry(Logger::FUNCTION_TAG, kProgramEntryName)),
875 gc_entry_(
876 profiles->NewCodeEntry(Logger::BUILTIN_TAG,
877 kGarbageCollectorEntryName)) {
878 }
879
880
RecordTickSample(const TickSample & sample)881 void ProfileGenerator::RecordTickSample(const TickSample& sample) {
882 // Allocate space for stack frames + pc + function + vm-state.
883 ScopedVector<CodeEntry*> entries(sample.frames_count + 3);
884 // As actual number of decoded code entries may vary, initialize
885 // entries vector with NULL values.
886 CodeEntry** entry = entries.start();
887 memset(entry, 0, entries.length() * sizeof(*entry));
888 if (sample.pc != NULL) {
889 *entry++ = code_map_.FindEntry(sample.pc);
890
891 if (sample.has_external_callback) {
892 // Don't use PC when in external callback code, as it can point
893 // inside callback's code, and we will erroneously report
894 // that a callback calls itself.
895 *(entries.start()) = NULL;
896 *entry++ = code_map_.FindEntry(sample.external_callback);
897 } else if (sample.tos != NULL) {
898 // Find out, if top of stack was pointing inside a JS function
899 // meaning that we have encountered a frameless invocation.
900 *entry = code_map_.FindEntry(sample.tos);
901 if (*entry != NULL && !(*entry)->is_js_function()) {
902 *entry = NULL;
903 }
904 entry++;
905 }
906
907 for (const Address* stack_pos = sample.stack,
908 *stack_end = stack_pos + sample.frames_count;
909 stack_pos != stack_end;
910 ++stack_pos) {
911 *entry++ = code_map_.FindEntry(*stack_pos);
912 }
913 }
914
915 if (FLAG_prof_browser_mode) {
916 bool no_symbolized_entries = true;
917 for (CodeEntry** e = entries.start(); e != entry; ++e) {
918 if (*e != NULL) {
919 no_symbolized_entries = false;
920 break;
921 }
922 }
923 // If no frames were symbolized, put the VM state entry in.
924 if (no_symbolized_entries) {
925 *entry++ = EntryForVMState(sample.state);
926 }
927 }
928
929 profiles_->AddPathToCurrentProfiles(entries);
930 }
931
932
Init(int child_index,Type type,const char * name,HeapEntry * to)933 void HeapGraphEdge::Init(
934 int child_index, Type type, const char* name, HeapEntry* to) {
935 ASSERT(type == kContextVariable
936 || type == kProperty
937 || type == kInternal
938 || type == kShortcut);
939 child_index_ = child_index;
940 type_ = type;
941 name_ = name;
942 to_ = to;
943 }
944
945
Init(int child_index,Type type,int index,HeapEntry * to)946 void HeapGraphEdge::Init(int child_index, Type type, int index, HeapEntry* to) {
947 ASSERT(type == kElement || type == kHidden || type == kWeak);
948 child_index_ = child_index;
949 type_ = type;
950 index_ = index;
951 to_ = to;
952 }
953
954
Init(int child_index,int index,HeapEntry * to)955 void HeapGraphEdge::Init(int child_index, int index, HeapEntry* to) {
956 Init(child_index, kElement, index, to);
957 }
958
959
From()960 HeapEntry* HeapGraphEdge::From() {
961 return reinterpret_cast<HeapEntry*>(this - child_index_) - 1;
962 }
963
964
Init(HeapSnapshot * snapshot,Type type,const char * name,SnapshotObjectId id,int self_size,int children_count,int retainers_count)965 void HeapEntry::Init(HeapSnapshot* snapshot,
966 Type type,
967 const char* name,
968 SnapshotObjectId id,
969 int self_size,
970 int children_count,
971 int retainers_count) {
972 snapshot_ = snapshot;
973 type_ = type;
974 painted_ = false;
975 name_ = name;
976 self_size_ = self_size;
977 retained_size_ = 0;
978 children_count_ = children_count;
979 retainers_count_ = retainers_count;
980 dominator_ = NULL;
981 id_ = id;
982 }
983
984
SetNamedReference(HeapGraphEdge::Type type,int child_index,const char * name,HeapEntry * entry,int retainer_index)985 void HeapEntry::SetNamedReference(HeapGraphEdge::Type type,
986 int child_index,
987 const char* name,
988 HeapEntry* entry,
989 int retainer_index) {
990 children()[child_index].Init(child_index, type, name, entry);
991 entry->retainers()[retainer_index] = children_arr() + child_index;
992 }
993
994
SetIndexedReference(HeapGraphEdge::Type type,int child_index,int index,HeapEntry * entry,int retainer_index)995 void HeapEntry::SetIndexedReference(HeapGraphEdge::Type type,
996 int child_index,
997 int index,
998 HeapEntry* entry,
999 int retainer_index) {
1000 children()[child_index].Init(child_index, type, index, entry);
1001 entry->retainers()[retainer_index] = children_arr() + child_index;
1002 }
1003
1004
SetUnidirElementReference(int child_index,int index,HeapEntry * entry)1005 void HeapEntry::SetUnidirElementReference(
1006 int child_index, int index, HeapEntry* entry) {
1007 children()[child_index].Init(child_index, index, entry);
1008 }
1009
1010
GetHeapObject()1011 Handle<HeapObject> HeapEntry::GetHeapObject() {
1012 return snapshot_->collection()->FindHeapObjectById(id());
1013 }
1014
1015
Print(const char * prefix,const char * edge_name,int max_depth,int indent)1016 void HeapEntry::Print(
1017 const char* prefix, const char* edge_name, int max_depth, int indent) {
1018 OS::Print("%6d %7d @%6llu %*c %s%s: ",
1019 self_size(), retained_size(), id(),
1020 indent, ' ', prefix, edge_name);
1021 if (type() != kString) {
1022 OS::Print("%s %.40s\n", TypeAsString(), name_);
1023 } else {
1024 OS::Print("\"");
1025 const char* c = name_;
1026 while (*c && (c - name_) <= 40) {
1027 if (*c != '\n')
1028 OS::Print("%c", *c);
1029 else
1030 OS::Print("\\n");
1031 ++c;
1032 }
1033 OS::Print("\"\n");
1034 }
1035 if (--max_depth == 0) return;
1036 Vector<HeapGraphEdge> ch = children();
1037 for (int i = 0; i < ch.length(); ++i) {
1038 HeapGraphEdge& edge = ch[i];
1039 const char* edge_prefix = "";
1040 EmbeddedVector<char, 64> index;
1041 const char* edge_name = index.start();
1042 switch (edge.type()) {
1043 case HeapGraphEdge::kContextVariable:
1044 edge_prefix = "#";
1045 edge_name = edge.name();
1046 break;
1047 case HeapGraphEdge::kElement:
1048 OS::SNPrintF(index, "%d", edge.index());
1049 break;
1050 case HeapGraphEdge::kInternal:
1051 edge_prefix = "$";
1052 edge_name = edge.name();
1053 break;
1054 case HeapGraphEdge::kProperty:
1055 edge_name = edge.name();
1056 break;
1057 case HeapGraphEdge::kHidden:
1058 edge_prefix = "$";
1059 OS::SNPrintF(index, "%d", edge.index());
1060 break;
1061 case HeapGraphEdge::kShortcut:
1062 edge_prefix = "^";
1063 edge_name = edge.name();
1064 break;
1065 case HeapGraphEdge::kWeak:
1066 edge_prefix = "w";
1067 OS::SNPrintF(index, "%d", edge.index());
1068 break;
1069 default:
1070 OS::SNPrintF(index, "!!! unknown edge type: %d ", edge.type());
1071 }
1072 edge.to()->Print(edge_prefix, edge_name, max_depth, indent + 2);
1073 }
1074 }
1075
1076
TypeAsString()1077 const char* HeapEntry::TypeAsString() {
1078 switch (type()) {
1079 case kHidden: return "/hidden/";
1080 case kObject: return "/object/";
1081 case kClosure: return "/closure/";
1082 case kString: return "/string/";
1083 case kCode: return "/code/";
1084 case kArray: return "/array/";
1085 case kRegExp: return "/regexp/";
1086 case kHeapNumber: return "/number/";
1087 case kNative: return "/native/";
1088 case kSynthetic: return "/synthetic/";
1089 default: return "???";
1090 }
1091 }
1092
1093
EntriesSize(int entries_count,int children_count,int retainers_count)1094 size_t HeapEntry::EntriesSize(int entries_count,
1095 int children_count,
1096 int retainers_count) {
1097 return sizeof(HeapEntry) * entries_count // NOLINT
1098 + sizeof(HeapGraphEdge) * children_count // NOLINT
1099 + sizeof(HeapGraphEdge*) * retainers_count; // NOLINT
1100 }
1101
1102
1103 // It is very important to keep objects that form a heap snapshot
1104 // as small as possible.
1105 namespace { // Avoid littering the global namespace.
1106
1107 template <size_t ptr_size> struct SnapshotSizeConstants;
1108
1109 template <> struct SnapshotSizeConstants<4> {
1110 static const int kExpectedHeapGraphEdgeSize = 12;
1111 static const int kExpectedHeapEntrySize = 32;
1112 static const size_t kMaxSerializableSnapshotRawSize = 256 * MB;
1113 };
1114
1115 template <> struct SnapshotSizeConstants<8> {
1116 static const int kExpectedHeapGraphEdgeSize = 24;
1117 static const int kExpectedHeapEntrySize = 48;
1118 static const uint64_t kMaxSerializableSnapshotRawSize =
1119 static_cast<uint64_t>(6000) * MB;
1120 };
1121
1122 } // namespace
1123
HeapSnapshot(HeapSnapshotsCollection * collection,HeapSnapshot::Type type,const char * title,unsigned uid)1124 HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
1125 HeapSnapshot::Type type,
1126 const char* title,
1127 unsigned uid)
1128 : collection_(collection),
1129 type_(type),
1130 title_(title),
1131 uid_(uid),
1132 root_entry_(NULL),
1133 gc_roots_entry_(NULL),
1134 natives_root_entry_(NULL),
1135 raw_entries_(NULL),
1136 entries_sorted_(false) {
1137 STATIC_CHECK(
1138 sizeof(HeapGraphEdge) ==
1139 SnapshotSizeConstants<kPointerSize>::kExpectedHeapGraphEdgeSize);
1140 STATIC_CHECK(
1141 sizeof(HeapEntry) ==
1142 SnapshotSizeConstants<kPointerSize>::kExpectedHeapEntrySize);
1143 for (int i = 0; i < VisitorSynchronization::kNumberOfSyncTags; ++i) {
1144 gc_subroot_entries_[i] = NULL;
1145 }
1146 }
1147
1148
~HeapSnapshot()1149 HeapSnapshot::~HeapSnapshot() {
1150 DeleteArray(raw_entries_);
1151 }
1152
1153
Delete()1154 void HeapSnapshot::Delete() {
1155 collection_->RemoveSnapshot(this);
1156 delete this;
1157 }
1158
1159
AllocateEntries(int entries_count,int children_count,int retainers_count)1160 void HeapSnapshot::AllocateEntries(int entries_count,
1161 int children_count,
1162 int retainers_count) {
1163 ASSERT(raw_entries_ == NULL);
1164 raw_entries_size_ =
1165 HeapEntry::EntriesSize(entries_count, children_count, retainers_count);
1166 raw_entries_ = NewArray<char>(raw_entries_size_);
1167 }
1168
1169
HeapEntryClearPaint(HeapEntry ** entry_ptr)1170 static void HeapEntryClearPaint(HeapEntry** entry_ptr) {
1171 (*entry_ptr)->clear_paint();
1172 }
1173
1174
ClearPaint()1175 void HeapSnapshot::ClearPaint() {
1176 entries_.Iterate(HeapEntryClearPaint);
1177 }
1178
1179
AddRootEntry(int children_count)1180 HeapEntry* HeapSnapshot::AddRootEntry(int children_count) {
1181 ASSERT(root_entry_ == NULL);
1182 return (root_entry_ = AddEntry(HeapEntry::kObject,
1183 "",
1184 HeapObjectsMap::kInternalRootObjectId,
1185 0,
1186 children_count,
1187 0));
1188 }
1189
1190
AddGcRootsEntry(int children_count,int retainers_count)1191 HeapEntry* HeapSnapshot::AddGcRootsEntry(int children_count,
1192 int retainers_count) {
1193 ASSERT(gc_roots_entry_ == NULL);
1194 return (gc_roots_entry_ = AddEntry(HeapEntry::kObject,
1195 "(GC roots)",
1196 HeapObjectsMap::kGcRootsObjectId,
1197 0,
1198 children_count,
1199 retainers_count));
1200 }
1201
1202
AddGcSubrootEntry(int tag,int children_count,int retainers_count)1203 HeapEntry* HeapSnapshot::AddGcSubrootEntry(int tag,
1204 int children_count,
1205 int retainers_count) {
1206 ASSERT(gc_subroot_entries_[tag] == NULL);
1207 ASSERT(0 <= tag && tag < VisitorSynchronization::kNumberOfSyncTags);
1208 return (gc_subroot_entries_[tag] = AddEntry(
1209 HeapEntry::kObject,
1210 VisitorSynchronization::kTagNames[tag],
1211 HeapObjectsMap::GetNthGcSubrootId(tag),
1212 0,
1213 children_count,
1214 retainers_count));
1215 }
1216
1217
AddEntry(HeapEntry::Type type,const char * name,SnapshotObjectId id,int size,int children_count,int retainers_count)1218 HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
1219 const char* name,
1220 SnapshotObjectId id,
1221 int size,
1222 int children_count,
1223 int retainers_count) {
1224 HeapEntry* entry = GetNextEntryToInit();
1225 entry->Init(this, type, name, id, size, children_count, retainers_count);
1226 return entry;
1227 }
1228
1229
SetDominatorsToSelf()1230 void HeapSnapshot::SetDominatorsToSelf() {
1231 for (int i = 0; i < entries_.length(); ++i) {
1232 HeapEntry* entry = entries_[i];
1233 if (entry->dominator() == NULL) entry->set_dominator(entry);
1234 }
1235 }
1236
1237
GetNextEntryToInit()1238 HeapEntry* HeapSnapshot::GetNextEntryToInit() {
1239 if (entries_.length() > 0) {
1240 HeapEntry* last_entry = entries_.last();
1241 entries_.Add(reinterpret_cast<HeapEntry*>(
1242 reinterpret_cast<char*>(last_entry) + last_entry->EntrySize()));
1243 } else {
1244 entries_.Add(reinterpret_cast<HeapEntry*>(raw_entries_));
1245 }
1246 ASSERT(reinterpret_cast<char*>(entries_.last()) <
1247 (raw_entries_ + raw_entries_size_));
1248 return entries_.last();
1249 }
1250
1251
GetEntryById(SnapshotObjectId id)1252 HeapEntry* HeapSnapshot::GetEntryById(SnapshotObjectId id) {
1253 List<HeapEntry*>* entries_by_id = GetSortedEntriesList();
1254
1255 // Perform a binary search by id.
1256 int low = 0;
1257 int high = entries_by_id->length() - 1;
1258 while (low <= high) {
1259 int mid =
1260 (static_cast<unsigned int>(low) + static_cast<unsigned int>(high)) >> 1;
1261 SnapshotObjectId mid_id = entries_by_id->at(mid)->id();
1262 if (mid_id > id)
1263 high = mid - 1;
1264 else if (mid_id < id)
1265 low = mid + 1;
1266 else
1267 return entries_by_id->at(mid);
1268 }
1269 return NULL;
1270 }
1271
1272
1273 template<class T>
SortByIds(const T * entry1_ptr,const T * entry2_ptr)1274 static int SortByIds(const T* entry1_ptr,
1275 const T* entry2_ptr) {
1276 if ((*entry1_ptr)->id() == (*entry2_ptr)->id()) return 0;
1277 return (*entry1_ptr)->id() < (*entry2_ptr)->id() ? -1 : 1;
1278 }
1279
1280
GetSortedEntriesList()1281 List<HeapEntry*>* HeapSnapshot::GetSortedEntriesList() {
1282 if (!entries_sorted_) {
1283 entries_.Sort(SortByIds);
1284 entries_sorted_ = true;
1285 }
1286 return &entries_;
1287 }
1288
1289
Print(int max_depth)1290 void HeapSnapshot::Print(int max_depth) {
1291 root()->Print("", "", max_depth, 0);
1292 }
1293
1294
1295 // We split IDs on evens for embedder objects (see
1296 // HeapObjectsMap::GenerateId) and odds for native objects.
1297 const SnapshotObjectId HeapObjectsMap::kInternalRootObjectId = 1;
1298 const SnapshotObjectId HeapObjectsMap::kGcRootsObjectId =
1299 HeapObjectsMap::kInternalRootObjectId + HeapObjectsMap::kObjectIdStep;
1300 const SnapshotObjectId HeapObjectsMap::kGcRootsFirstSubrootId =
1301 HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep;
1302 const SnapshotObjectId HeapObjectsMap::kFirstAvailableObjectId =
1303 HeapObjectsMap::kGcRootsFirstSubrootId +
1304 VisitorSynchronization::kNumberOfSyncTags * HeapObjectsMap::kObjectIdStep;
1305
HeapObjectsMap()1306 HeapObjectsMap::HeapObjectsMap()
1307 : initial_fill_mode_(true),
1308 next_id_(kFirstAvailableObjectId),
1309 entries_map_(AddressesMatch),
1310 entries_(new List<EntryInfo>()) { }
1311
1312
~HeapObjectsMap()1313 HeapObjectsMap::~HeapObjectsMap() {
1314 delete entries_;
1315 }
1316
1317
SnapshotGenerationFinished()1318 void HeapObjectsMap::SnapshotGenerationFinished() {
1319 initial_fill_mode_ = false;
1320 RemoveDeadEntries();
1321 }
1322
1323
FindObject(Address addr)1324 SnapshotObjectId HeapObjectsMap::FindObject(Address addr) {
1325 if (!initial_fill_mode_) {
1326 SnapshotObjectId existing = FindEntry(addr);
1327 if (existing != 0) return existing;
1328 }
1329 SnapshotObjectId id = next_id_;
1330 next_id_ += kObjectIdStep;
1331 AddEntry(addr, id);
1332 return id;
1333 }
1334
1335
MoveObject(Address from,Address to)1336 void HeapObjectsMap::MoveObject(Address from, Address to) {
1337 if (from == to) return;
1338 HashMap::Entry* entry = entries_map_.Lookup(from, AddressHash(from), false);
1339 if (entry != NULL) {
1340 void* value = entry->value;
1341 entries_map_.Remove(from, AddressHash(from));
1342 if (to != NULL) {
1343 entry = entries_map_.Lookup(to, AddressHash(to), true);
1344 // We can have an entry at the new location, it is OK, as GC can overwrite
1345 // dead objects with alive objects being moved.
1346 entry->value = value;
1347 }
1348 }
1349 }
1350
1351
AddEntry(Address addr,SnapshotObjectId id)1352 void HeapObjectsMap::AddEntry(Address addr, SnapshotObjectId id) {
1353 HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), true);
1354 ASSERT(entry->value == NULL);
1355 entry->value = reinterpret_cast<void*>(entries_->length());
1356 entries_->Add(EntryInfo(id));
1357 }
1358
1359
FindEntry(Address addr)1360 SnapshotObjectId HeapObjectsMap::FindEntry(Address addr) {
1361 HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), false);
1362 if (entry != NULL) {
1363 int entry_index =
1364 static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
1365 EntryInfo& entry_info = entries_->at(entry_index);
1366 entry_info.accessed = true;
1367 return entry_info.id;
1368 } else {
1369 return 0;
1370 }
1371 }
1372
1373
RemoveDeadEntries()1374 void HeapObjectsMap::RemoveDeadEntries() {
1375 List<EntryInfo>* new_entries = new List<EntryInfo>();
1376 List<void*> dead_entries;
1377 for (HashMap::Entry* entry = entries_map_.Start();
1378 entry != NULL;
1379 entry = entries_map_.Next(entry)) {
1380 int entry_index =
1381 static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
1382 EntryInfo& entry_info = entries_->at(entry_index);
1383 if (entry_info.accessed) {
1384 entry->value = reinterpret_cast<void*>(new_entries->length());
1385 new_entries->Add(EntryInfo(entry_info.id, false));
1386 } else {
1387 dead_entries.Add(entry->key);
1388 }
1389 }
1390 for (int i = 0; i < dead_entries.length(); ++i) {
1391 void* raw_entry = dead_entries[i];
1392 entries_map_.Remove(
1393 raw_entry, AddressHash(reinterpret_cast<Address>(raw_entry)));
1394 }
1395 delete entries_;
1396 entries_ = new_entries;
1397 }
1398
1399
GenerateId(v8::RetainedObjectInfo * info)1400 SnapshotObjectId HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) {
1401 SnapshotObjectId id = static_cast<SnapshotObjectId>(info->GetHash());
1402 const char* label = info->GetLabel();
1403 id ^= HashSequentialString(label,
1404 static_cast<int>(strlen(label)),
1405 HEAP->HashSeed());
1406 intptr_t element_count = info->GetElementCount();
1407 if (element_count != -1)
1408 id ^= ComputeIntegerHash(static_cast<uint32_t>(element_count),
1409 v8::internal::kZeroHashSeed);
1410 return id << 1;
1411 }
1412
1413
HeapSnapshotsCollection()1414 HeapSnapshotsCollection::HeapSnapshotsCollection()
1415 : is_tracking_objects_(false),
1416 snapshots_uids_(HeapSnapshotsMatch),
1417 token_enumerator_(new TokenEnumerator()) {
1418 }
1419
1420
DeleteHeapSnapshot(HeapSnapshot ** snapshot_ptr)1421 static void DeleteHeapSnapshot(HeapSnapshot** snapshot_ptr) {
1422 delete *snapshot_ptr;
1423 }
1424
1425
~HeapSnapshotsCollection()1426 HeapSnapshotsCollection::~HeapSnapshotsCollection() {
1427 delete token_enumerator_;
1428 snapshots_.Iterate(DeleteHeapSnapshot);
1429 }
1430
1431
NewSnapshot(HeapSnapshot::Type type,const char * name,unsigned uid)1432 HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(HeapSnapshot::Type type,
1433 const char* name,
1434 unsigned uid) {
1435 is_tracking_objects_ = true; // Start watching for heap objects moves.
1436 return new HeapSnapshot(this, type, name, uid);
1437 }
1438
1439
SnapshotGenerationFinished(HeapSnapshot * snapshot)1440 void HeapSnapshotsCollection::SnapshotGenerationFinished(
1441 HeapSnapshot* snapshot) {
1442 ids_.SnapshotGenerationFinished();
1443 if (snapshot != NULL) {
1444 snapshots_.Add(snapshot);
1445 HashMap::Entry* entry =
1446 snapshots_uids_.Lookup(reinterpret_cast<void*>(snapshot->uid()),
1447 static_cast<uint32_t>(snapshot->uid()),
1448 true);
1449 ASSERT(entry->value == NULL);
1450 entry->value = snapshot;
1451 }
1452 }
1453
1454
GetSnapshot(unsigned uid)1455 HeapSnapshot* HeapSnapshotsCollection::GetSnapshot(unsigned uid) {
1456 HashMap::Entry* entry = snapshots_uids_.Lookup(reinterpret_cast<void*>(uid),
1457 static_cast<uint32_t>(uid),
1458 false);
1459 return entry != NULL ? reinterpret_cast<HeapSnapshot*>(entry->value) : NULL;
1460 }
1461
1462
RemoveSnapshot(HeapSnapshot * snapshot)1463 void HeapSnapshotsCollection::RemoveSnapshot(HeapSnapshot* snapshot) {
1464 snapshots_.RemoveElement(snapshot);
1465 unsigned uid = snapshot->uid();
1466 snapshots_uids_.Remove(reinterpret_cast<void*>(uid),
1467 static_cast<uint32_t>(uid));
1468 }
1469
1470
FindHeapObjectById(SnapshotObjectId id)1471 Handle<HeapObject> HeapSnapshotsCollection::FindHeapObjectById(
1472 SnapshotObjectId id) {
1473 // First perform a full GC in order to avoid dead objects.
1474 HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask,
1475 "HeapSnapshotsCollection::FindHeapObjectById");
1476 AssertNoAllocation no_allocation;
1477 HeapObject* object = NULL;
1478 HeapIterator iterator(HeapIterator::kFilterUnreachable);
1479 // Make sure that object with the given id is still reachable.
1480 for (HeapObject* obj = iterator.next();
1481 obj != NULL;
1482 obj = iterator.next()) {
1483 if (ids_.FindObject(obj->address()) == id) {
1484 ASSERT(object == NULL);
1485 object = obj;
1486 // Can't break -- kFilterUnreachable requires full heap traversal.
1487 }
1488 }
1489 return object != NULL ? Handle<HeapObject>(object) : Handle<HeapObject>();
1490 }
1491
1492
1493 HeapEntry* const HeapEntriesMap::kHeapEntryPlaceholder =
1494 reinterpret_cast<HeapEntry*>(1);
1495
HeapEntriesMap()1496 HeapEntriesMap::HeapEntriesMap()
1497 : entries_(HeapThingsMatch),
1498 entries_count_(0),
1499 total_children_count_(0),
1500 total_retainers_count_(0) {
1501 }
1502
1503
~HeapEntriesMap()1504 HeapEntriesMap::~HeapEntriesMap() {
1505 for (HashMap::Entry* p = entries_.Start(); p != NULL; p = entries_.Next(p)) {
1506 delete reinterpret_cast<EntryInfo*>(p->value);
1507 }
1508 }
1509
1510
AllocateEntries()1511 void HeapEntriesMap::AllocateEntries() {
1512 for (HashMap::Entry* p = entries_.Start();
1513 p != NULL;
1514 p = entries_.Next(p)) {
1515 EntryInfo* entry_info = reinterpret_cast<EntryInfo*>(p->value);
1516 entry_info->entry = entry_info->allocator->AllocateEntry(
1517 p->key,
1518 entry_info->children_count,
1519 entry_info->retainers_count);
1520 ASSERT(entry_info->entry != NULL);
1521 ASSERT(entry_info->entry != kHeapEntryPlaceholder);
1522 entry_info->children_count = 0;
1523 entry_info->retainers_count = 0;
1524 }
1525 }
1526
1527
Map(HeapThing thing)1528 HeapEntry* HeapEntriesMap::Map(HeapThing thing) {
1529 HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), false);
1530 if (cache_entry != NULL) {
1531 EntryInfo* entry_info = reinterpret_cast<EntryInfo*>(cache_entry->value);
1532 return entry_info->entry;
1533 } else {
1534 return NULL;
1535 }
1536 }
1537
1538
Pair(HeapThing thing,HeapEntriesAllocator * allocator,HeapEntry * entry)1539 void HeapEntriesMap::Pair(
1540 HeapThing thing, HeapEntriesAllocator* allocator, HeapEntry* entry) {
1541 HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), true);
1542 ASSERT(cache_entry->value == NULL);
1543 cache_entry->value = new EntryInfo(entry, allocator);
1544 ++entries_count_;
1545 }
1546
1547
CountReference(HeapThing from,HeapThing to,int * prev_children_count,int * prev_retainers_count)1548 void HeapEntriesMap::CountReference(HeapThing from, HeapThing to,
1549 int* prev_children_count,
1550 int* prev_retainers_count) {
1551 HashMap::Entry* from_cache_entry = entries_.Lookup(from, Hash(from), false);
1552 HashMap::Entry* to_cache_entry = entries_.Lookup(to, Hash(to), false);
1553 ASSERT(from_cache_entry != NULL);
1554 ASSERT(to_cache_entry != NULL);
1555 EntryInfo* from_entry_info =
1556 reinterpret_cast<EntryInfo*>(from_cache_entry->value);
1557 EntryInfo* to_entry_info =
1558 reinterpret_cast<EntryInfo*>(to_cache_entry->value);
1559 if (prev_children_count)
1560 *prev_children_count = from_entry_info->children_count;
1561 if (prev_retainers_count)
1562 *prev_retainers_count = to_entry_info->retainers_count;
1563 ++from_entry_info->children_count;
1564 ++to_entry_info->retainers_count;
1565 ++total_children_count_;
1566 ++total_retainers_count_;
1567 }
1568
1569
HeapObjectsSet()1570 HeapObjectsSet::HeapObjectsSet()
1571 : entries_(HeapEntriesMap::HeapThingsMatch) {
1572 }
1573
1574
Clear()1575 void HeapObjectsSet::Clear() {
1576 entries_.Clear();
1577 }
1578
1579
Contains(Object * obj)1580 bool HeapObjectsSet::Contains(Object* obj) {
1581 if (!obj->IsHeapObject()) return false;
1582 HeapObject* object = HeapObject::cast(obj);
1583 HashMap::Entry* cache_entry =
1584 entries_.Lookup(object, HeapEntriesMap::Hash(object), false);
1585 return cache_entry != NULL;
1586 }
1587
1588
Insert(Object * obj)1589 void HeapObjectsSet::Insert(Object* obj) {
1590 if (!obj->IsHeapObject()) return;
1591 HeapObject* object = HeapObject::cast(obj);
1592 HashMap::Entry* cache_entry =
1593 entries_.Lookup(object, HeapEntriesMap::Hash(object), true);
1594 if (cache_entry->value == NULL) {
1595 cache_entry->value = HeapEntriesMap::kHeapEntryPlaceholder;
1596 }
1597 }
1598
1599
GetTag(Object * obj)1600 const char* HeapObjectsSet::GetTag(Object* obj) {
1601 HeapObject* object = HeapObject::cast(obj);
1602 HashMap::Entry* cache_entry =
1603 entries_.Lookup(object, HeapEntriesMap::Hash(object), false);
1604 if (cache_entry != NULL
1605 && cache_entry->value != HeapEntriesMap::kHeapEntryPlaceholder) {
1606 return reinterpret_cast<const char*>(cache_entry->value);
1607 } else {
1608 return NULL;
1609 }
1610 }
1611
1612
SetTag(Object * obj,const char * tag)1613 void HeapObjectsSet::SetTag(Object* obj, const char* tag) {
1614 if (!obj->IsHeapObject()) return;
1615 HeapObject* object = HeapObject::cast(obj);
1616 HashMap::Entry* cache_entry =
1617 entries_.Lookup(object, HeapEntriesMap::Hash(object), true);
1618 cache_entry->value = const_cast<char*>(tag);
1619 }
1620
1621
1622 HeapObject* const V8HeapExplorer::kInternalRootObject =
1623 reinterpret_cast<HeapObject*>(
1624 static_cast<intptr_t>(HeapObjectsMap::kInternalRootObjectId));
1625 HeapObject* const V8HeapExplorer::kGcRootsObject =
1626 reinterpret_cast<HeapObject*>(
1627 static_cast<intptr_t>(HeapObjectsMap::kGcRootsObjectId));
1628 HeapObject* const V8HeapExplorer::kFirstGcSubrootObject =
1629 reinterpret_cast<HeapObject*>(
1630 static_cast<intptr_t>(HeapObjectsMap::kGcRootsFirstSubrootId));
1631 HeapObject* const V8HeapExplorer::kLastGcSubrootObject =
1632 reinterpret_cast<HeapObject*>(
1633 static_cast<intptr_t>(HeapObjectsMap::kFirstAvailableObjectId));
1634
1635
V8HeapExplorer(HeapSnapshot * snapshot,SnapshottingProgressReportingInterface * progress)1636 V8HeapExplorer::V8HeapExplorer(
1637 HeapSnapshot* snapshot,
1638 SnapshottingProgressReportingInterface* progress)
1639 : heap_(Isolate::Current()->heap()),
1640 snapshot_(snapshot),
1641 collection_(snapshot_->collection()),
1642 progress_(progress),
1643 filler_(NULL) {
1644 }
1645
1646
~V8HeapExplorer()1647 V8HeapExplorer::~V8HeapExplorer() {
1648 }
1649
1650
AllocateEntry(HeapThing ptr,int children_count,int retainers_count)1651 HeapEntry* V8HeapExplorer::AllocateEntry(
1652 HeapThing ptr, int children_count, int retainers_count) {
1653 return AddEntry(
1654 reinterpret_cast<HeapObject*>(ptr), children_count, retainers_count);
1655 }
1656
1657
AddEntry(HeapObject * object,int children_count,int retainers_count)1658 HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
1659 int children_count,
1660 int retainers_count) {
1661 if (object == kInternalRootObject) {
1662 ASSERT(retainers_count == 0);
1663 return snapshot_->AddRootEntry(children_count);
1664 } else if (object == kGcRootsObject) {
1665 return snapshot_->AddGcRootsEntry(children_count, retainers_count);
1666 } else if (object >= kFirstGcSubrootObject && object < kLastGcSubrootObject) {
1667 return snapshot_->AddGcSubrootEntry(
1668 GetGcSubrootOrder(object),
1669 children_count,
1670 retainers_count);
1671 } else if (object->IsJSFunction()) {
1672 JSFunction* func = JSFunction::cast(object);
1673 SharedFunctionInfo* shared = func->shared();
1674 const char* name = shared->bound() ? "native_bind" :
1675 collection_->names()->GetName(String::cast(shared->name()));
1676 return AddEntry(object,
1677 HeapEntry::kClosure,
1678 name,
1679 children_count,
1680 retainers_count);
1681 } else if (object->IsJSRegExp()) {
1682 JSRegExp* re = JSRegExp::cast(object);
1683 return AddEntry(object,
1684 HeapEntry::kRegExp,
1685 collection_->names()->GetName(re->Pattern()),
1686 children_count,
1687 retainers_count);
1688 } else if (object->IsJSObject()) {
1689 return AddEntry(object,
1690 HeapEntry::kObject,
1691 "",
1692 children_count,
1693 retainers_count);
1694 } else if (object->IsString()) {
1695 return AddEntry(object,
1696 HeapEntry::kString,
1697 collection_->names()->GetName(String::cast(object)),
1698 children_count,
1699 retainers_count);
1700 } else if (object->IsCode()) {
1701 return AddEntry(object,
1702 HeapEntry::kCode,
1703 "",
1704 children_count,
1705 retainers_count);
1706 } else if (object->IsSharedFunctionInfo()) {
1707 SharedFunctionInfo* shared = SharedFunctionInfo::cast(object);
1708 return AddEntry(object,
1709 HeapEntry::kCode,
1710 collection_->names()->GetName(String::cast(shared->name())),
1711 children_count,
1712 retainers_count);
1713 } else if (object->IsScript()) {
1714 Script* script = Script::cast(object);
1715 return AddEntry(object,
1716 HeapEntry::kCode,
1717 script->name()->IsString() ?
1718 collection_->names()->GetName(
1719 String::cast(script->name()))
1720 : "",
1721 children_count,
1722 retainers_count);
1723 } else if (object->IsGlobalContext()) {
1724 return AddEntry(object,
1725 HeapEntry::kHidden,
1726 "system / GlobalContext",
1727 children_count,
1728 retainers_count);
1729 } else if (object->IsContext()) {
1730 return AddEntry(object,
1731 HeapEntry::kHidden,
1732 "system / Context",
1733 children_count,
1734 retainers_count);
1735 } else if (object->IsFixedArray() ||
1736 object->IsFixedDoubleArray() ||
1737 object->IsByteArray() ||
1738 object->IsExternalArray()) {
1739 const char* tag = objects_tags_.GetTag(object);
1740 return AddEntry(object,
1741 HeapEntry::kArray,
1742 tag != NULL ? tag : "",
1743 children_count,
1744 retainers_count);
1745 } else if (object->IsHeapNumber()) {
1746 return AddEntry(object,
1747 HeapEntry::kHeapNumber,
1748 "number",
1749 children_count,
1750 retainers_count);
1751 }
1752 return AddEntry(object,
1753 HeapEntry::kHidden,
1754 GetSystemEntryName(object),
1755 children_count,
1756 retainers_count);
1757 }
1758
1759
AddEntry(HeapObject * object,HeapEntry::Type type,const char * name,int children_count,int retainers_count)1760 HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
1761 HeapEntry::Type type,
1762 const char* name,
1763 int children_count,
1764 int retainers_count) {
1765 return snapshot_->AddEntry(type,
1766 name,
1767 collection_->GetObjectId(object->address()),
1768 object->Size(),
1769 children_count,
1770 retainers_count);
1771 }
1772
1773
1774 class GcSubrootsEnumerator : public ObjectVisitor {
1775 public:
GcSubrootsEnumerator(SnapshotFillerInterface * filler,V8HeapExplorer * explorer)1776 GcSubrootsEnumerator(
1777 SnapshotFillerInterface* filler, V8HeapExplorer* explorer)
1778 : filler_(filler),
1779 explorer_(explorer),
1780 previous_object_count_(0),
1781 object_count_(0) {
1782 }
VisitPointers(Object ** start,Object ** end)1783 void VisitPointers(Object** start, Object** end) {
1784 object_count_ += end - start;
1785 }
Synchronize(VisitorSynchronization::SyncTag tag)1786 void Synchronize(VisitorSynchronization::SyncTag tag) {
1787 // Skip empty subroots.
1788 if (previous_object_count_ != object_count_) {
1789 previous_object_count_ = object_count_;
1790 filler_->AddEntry(V8HeapExplorer::GetNthGcSubrootObject(tag), explorer_);
1791 }
1792 }
1793 private:
1794 SnapshotFillerInterface* filler_;
1795 V8HeapExplorer* explorer_;
1796 intptr_t previous_object_count_;
1797 intptr_t object_count_;
1798 };
1799
1800
AddRootEntries(SnapshotFillerInterface * filler)1801 void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) {
1802 filler->AddEntry(kInternalRootObject, this);
1803 filler->AddEntry(kGcRootsObject, this);
1804 GcSubrootsEnumerator enumerator(filler, this);
1805 heap_->IterateRoots(&enumerator, VISIT_ALL);
1806 }
1807
1808
GetSystemEntryName(HeapObject * object)1809 const char* V8HeapExplorer::GetSystemEntryName(HeapObject* object) {
1810 switch (object->map()->instance_type()) {
1811 case MAP_TYPE: return "system / Map";
1812 case JS_GLOBAL_PROPERTY_CELL_TYPE: return "system / JSGlobalPropertyCell";
1813 case FOREIGN_TYPE: return "system / Foreign";
1814 case ODDBALL_TYPE: return "system / Oddball";
1815 #define MAKE_STRUCT_CASE(NAME, Name, name) \
1816 case NAME##_TYPE: return "system / "#Name;
1817 STRUCT_LIST(MAKE_STRUCT_CASE)
1818 #undef MAKE_STRUCT_CASE
1819 default: return "system";
1820 }
1821 }
1822
1823
EstimateObjectsCount(HeapIterator * iterator)1824 int V8HeapExplorer::EstimateObjectsCount(HeapIterator* iterator) {
1825 int objects_count = 0;
1826 for (HeapObject* obj = iterator->next();
1827 obj != NULL;
1828 obj = iterator->next()) {
1829 objects_count++;
1830 }
1831 return objects_count;
1832 }
1833
1834
1835 class IndexedReferencesExtractor : public ObjectVisitor {
1836 public:
IndexedReferencesExtractor(V8HeapExplorer * generator,HeapObject * parent_obj,HeapEntry * parent_entry)1837 IndexedReferencesExtractor(V8HeapExplorer* generator,
1838 HeapObject* parent_obj,
1839 HeapEntry* parent_entry)
1840 : generator_(generator),
1841 parent_obj_(parent_obj),
1842 parent_(parent_entry),
1843 next_index_(1) {
1844 }
VisitPointers(Object ** start,Object ** end)1845 void VisitPointers(Object** start, Object** end) {
1846 for (Object** p = start; p < end; p++) {
1847 if (CheckVisitedAndUnmark(p)) continue;
1848 generator_->SetHiddenReference(parent_obj_, parent_, next_index_++, *p);
1849 }
1850 }
MarkVisitedField(HeapObject * obj,int offset)1851 static void MarkVisitedField(HeapObject* obj, int offset) {
1852 if (offset < 0) return;
1853 Address field = obj->address() + offset;
1854 ASSERT(!Memory::Object_at(field)->IsFailure());
1855 ASSERT(Memory::Object_at(field)->IsHeapObject());
1856 *field |= kFailureTag;
1857 }
1858
1859 private:
CheckVisitedAndUnmark(Object ** field)1860 bool CheckVisitedAndUnmark(Object** field) {
1861 if ((*field)->IsFailure()) {
1862 intptr_t untagged = reinterpret_cast<intptr_t>(*field) & ~kFailureTagMask;
1863 *field = reinterpret_cast<Object*>(untagged | kHeapObjectTag);
1864 ASSERT((*field)->IsHeapObject());
1865 return true;
1866 }
1867 return false;
1868 }
1869 V8HeapExplorer* generator_;
1870 HeapObject* parent_obj_;
1871 HeapEntry* parent_;
1872 int next_index_;
1873 };
1874
1875
ExtractReferences(HeapObject * obj)1876 void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
1877 HeapEntry* entry = GetEntry(obj);
1878 if (entry == NULL) return; // No interest in this object.
1879
1880 bool extract_indexed_refs = true;
1881 if (obj->IsJSGlobalProxy()) {
1882 // We need to reference JS global objects from snapshot's root.
1883 // We use JSGlobalProxy because this is what embedder (e.g. browser)
1884 // uses for the global object.
1885 JSGlobalProxy* proxy = JSGlobalProxy::cast(obj);
1886 SetRootShortcutReference(proxy->map()->prototype());
1887 } else if (obj->IsJSObject()) {
1888 JSObject* js_obj = JSObject::cast(obj);
1889 ExtractClosureReferences(js_obj, entry);
1890 ExtractPropertyReferences(js_obj, entry);
1891 ExtractElementReferences(js_obj, entry);
1892 ExtractInternalReferences(js_obj, entry);
1893 SetPropertyReference(
1894 obj, entry, heap_->Proto_symbol(), js_obj->GetPrototype());
1895 if (obj->IsJSFunction()) {
1896 JSFunction* js_fun = JSFunction::cast(js_obj);
1897 Object* proto_or_map = js_fun->prototype_or_initial_map();
1898 if (!proto_or_map->IsTheHole()) {
1899 if (!proto_or_map->IsMap()) {
1900 SetPropertyReference(
1901 obj, entry,
1902 heap_->prototype_symbol(), proto_or_map,
1903 NULL,
1904 JSFunction::kPrototypeOrInitialMapOffset);
1905 } else {
1906 SetPropertyReference(
1907 obj, entry,
1908 heap_->prototype_symbol(), js_fun->prototype());
1909 }
1910 }
1911 SharedFunctionInfo* shared_info = js_fun->shared();
1912 // JSFunction has either bindings or literals and never both.
1913 bool bound = shared_info->bound();
1914 TagObject(js_fun->literals_or_bindings(),
1915 bound ? "(function bindings)" : "(function literals)");
1916 SetInternalReference(js_fun, entry,
1917 bound ? "bindings" : "literals",
1918 js_fun->literals_or_bindings(),
1919 JSFunction::kLiteralsOffset);
1920 SetInternalReference(js_fun, entry,
1921 "shared", shared_info,
1922 JSFunction::kSharedFunctionInfoOffset);
1923 TagObject(js_fun->unchecked_context(), "(context)");
1924 SetInternalReference(js_fun, entry,
1925 "context", js_fun->unchecked_context(),
1926 JSFunction::kContextOffset);
1927 for (int i = JSFunction::kNonWeakFieldsEndOffset;
1928 i < JSFunction::kSize;
1929 i += kPointerSize) {
1930 SetWeakReference(js_fun, entry, i, *HeapObject::RawField(js_fun, i), i);
1931 }
1932 }
1933 TagObject(js_obj->properties(), "(object properties)");
1934 SetInternalReference(obj, entry,
1935 "properties", js_obj->properties(),
1936 JSObject::kPropertiesOffset);
1937 TagObject(js_obj->elements(), "(object elements)");
1938 SetInternalReference(obj, entry,
1939 "elements", js_obj->elements(),
1940 JSObject::kElementsOffset);
1941 } else if (obj->IsString()) {
1942 if (obj->IsConsString()) {
1943 ConsString* cs = ConsString::cast(obj);
1944 SetInternalReference(obj, entry, 1, cs->first());
1945 SetInternalReference(obj, entry, 2, cs->second());
1946 }
1947 if (obj->IsSlicedString()) {
1948 SlicedString* ss = SlicedString::cast(obj);
1949 SetInternalReference(obj, entry, "parent", ss->parent());
1950 }
1951 extract_indexed_refs = false;
1952 } else if (obj->IsGlobalContext()) {
1953 Context* context = Context::cast(obj);
1954 TagObject(context->jsfunction_result_caches(),
1955 "(context func. result caches)");
1956 TagObject(context->normalized_map_cache(), "(context norm. map cache)");
1957 TagObject(context->runtime_context(), "(runtime context)");
1958 TagObject(context->data(), "(context data)");
1959 for (int i = Context::FIRST_WEAK_SLOT;
1960 i < Context::GLOBAL_CONTEXT_SLOTS;
1961 ++i) {
1962 SetWeakReference(obj, entry,
1963 i, context->get(i),
1964 FixedArray::OffsetOfElementAt(i));
1965 }
1966 } else if (obj->IsMap()) {
1967 Map* map = Map::cast(obj);
1968 SetInternalReference(obj, entry,
1969 "prototype", map->prototype(), Map::kPrototypeOffset);
1970 SetInternalReference(obj, entry,
1971 "constructor", map->constructor(),
1972 Map::kConstructorOffset);
1973 if (!map->instance_descriptors()->IsEmpty()) {
1974 TagObject(map->instance_descriptors(), "(map descriptors)");
1975 SetInternalReference(obj, entry,
1976 "descriptors", map->instance_descriptors(),
1977 Map::kInstanceDescriptorsOrBitField3Offset);
1978 }
1979 if (map->prototype_transitions() != heap_->empty_fixed_array()) {
1980 TagObject(map->prototype_transitions(), "(prototype transitions)");
1981 SetInternalReference(obj,
1982 entry,
1983 "prototype_transitions",
1984 map->prototype_transitions(),
1985 Map::kPrototypeTransitionsOffset);
1986 }
1987 SetInternalReference(obj, entry,
1988 "code_cache", map->code_cache(),
1989 Map::kCodeCacheOffset);
1990 } else if (obj->IsSharedFunctionInfo()) {
1991 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
1992 SetInternalReference(obj, entry,
1993 "name", shared->name(),
1994 SharedFunctionInfo::kNameOffset);
1995 SetInternalReference(obj, entry,
1996 "code", shared->unchecked_code(),
1997 SharedFunctionInfo::kCodeOffset);
1998 TagObject(shared->scope_info(), "(function scope info)");
1999 SetInternalReference(obj, entry,
2000 "scope_info", shared->scope_info(),
2001 SharedFunctionInfo::kScopeInfoOffset);
2002 SetInternalReference(obj, entry,
2003 "instance_class_name", shared->instance_class_name(),
2004 SharedFunctionInfo::kInstanceClassNameOffset);
2005 SetInternalReference(obj, entry,
2006 "script", shared->script(),
2007 SharedFunctionInfo::kScriptOffset);
2008 SetWeakReference(obj, entry,
2009 1, shared->initial_map(),
2010 SharedFunctionInfo::kInitialMapOffset);
2011 } else if (obj->IsScript()) {
2012 Script* script = Script::cast(obj);
2013 SetInternalReference(obj, entry,
2014 "source", script->source(),
2015 Script::kSourceOffset);
2016 SetInternalReference(obj, entry,
2017 "name", script->name(),
2018 Script::kNameOffset);
2019 SetInternalReference(obj, entry,
2020 "data", script->data(),
2021 Script::kDataOffset);
2022 SetInternalReference(obj, entry,
2023 "context_data", script->context_data(),
2024 Script::kContextOffset);
2025 TagObject(script->line_ends(), "(script line ends)");
2026 SetInternalReference(obj, entry,
2027 "line_ends", script->line_ends(),
2028 Script::kLineEndsOffset);
2029 } else if (obj->IsCodeCache()) {
2030 CodeCache* code_cache = CodeCache::cast(obj);
2031 TagObject(code_cache->default_cache(), "(default code cache)");
2032 SetInternalReference(obj, entry,
2033 "default_cache", code_cache->default_cache(),
2034 CodeCache::kDefaultCacheOffset);
2035 TagObject(code_cache->normal_type_cache(), "(code type cache)");
2036 SetInternalReference(obj, entry,
2037 "type_cache", code_cache->normal_type_cache(),
2038 CodeCache::kNormalTypeCacheOffset);
2039 } else if (obj->IsCode()) {
2040 Code* code = Code::cast(obj);
2041 TagObject(code->unchecked_relocation_info(), "(code relocation info)");
2042 TagObject(code->unchecked_deoptimization_data(), "(code deopt data)");
2043 }
2044 if (extract_indexed_refs) {
2045 SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset);
2046 IndexedReferencesExtractor refs_extractor(this, obj, entry);
2047 obj->Iterate(&refs_extractor);
2048 }
2049 }
2050
2051
ExtractClosureReferences(JSObject * js_obj,HeapEntry * entry)2052 void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj,
2053 HeapEntry* entry) {
2054 if (!js_obj->IsJSFunction()) return;
2055
2056 JSFunction* func = JSFunction::cast(js_obj);
2057 Context* context = func->context();
2058 ScopeInfo* scope_info = context->closure()->shared()->scope_info();
2059
2060 if (func->shared()->bound()) {
2061 FixedArray* bindings = func->function_bindings();
2062 SetNativeBindReference(js_obj, entry, "bound_this",
2063 bindings->get(JSFunction::kBoundThisIndex));
2064 SetNativeBindReference(js_obj, entry, "bound_function",
2065 bindings->get(JSFunction::kBoundFunctionIndex));
2066 for (int i = JSFunction::kBoundArgumentsStartIndex;
2067 i < bindings->length(); i++) {
2068 const char* reference_name = collection_->names()->GetFormatted(
2069 "bound_argument_%d",
2070 i - JSFunction::kBoundArgumentsStartIndex);
2071 SetNativeBindReference(js_obj, entry, reference_name,
2072 bindings->get(i));
2073 }
2074 } else {
2075 // Add context allocated locals.
2076 int context_locals = scope_info->ContextLocalCount();
2077 for (int i = 0; i < context_locals; ++i) {
2078 String* local_name = scope_info->ContextLocalName(i);
2079 int idx = Context::MIN_CONTEXT_SLOTS + i;
2080 SetClosureReference(js_obj, entry, local_name, context->get(idx));
2081 }
2082
2083 // Add function variable.
2084 if (scope_info->HasFunctionName()) {
2085 String* name = scope_info->FunctionName();
2086 int idx = Context::MIN_CONTEXT_SLOTS + context_locals;
2087 #ifdef DEBUG
2088 VariableMode mode;
2089 ASSERT(idx == scope_info->FunctionContextSlotIndex(name, &mode));
2090 #endif
2091 SetClosureReference(js_obj, entry, name, context->get(idx));
2092 }
2093 }
2094 }
2095
2096
ExtractPropertyReferences(JSObject * js_obj,HeapEntry * entry)2097 void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj,
2098 HeapEntry* entry) {
2099 if (js_obj->HasFastProperties()) {
2100 DescriptorArray* descs = js_obj->map()->instance_descriptors();
2101 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2102 switch (descs->GetType(i)) {
2103 case FIELD: {
2104 int index = descs->GetFieldIndex(i);
2105 if (index < js_obj->map()->inobject_properties()) {
2106 SetPropertyReference(
2107 js_obj, entry,
2108 descs->GetKey(i), js_obj->InObjectPropertyAt(index),
2109 NULL,
2110 js_obj->GetInObjectPropertyOffset(index));
2111 } else {
2112 SetPropertyReference(
2113 js_obj, entry,
2114 descs->GetKey(i), js_obj->FastPropertyAt(index));
2115 }
2116 break;
2117 }
2118 case CONSTANT_FUNCTION:
2119 SetPropertyReference(
2120 js_obj, entry,
2121 descs->GetKey(i), descs->GetConstantFunction(i));
2122 break;
2123 case CALLBACKS: {
2124 Object* callback_obj = descs->GetValue(i);
2125 if (callback_obj->IsAccessorPair()) {
2126 AccessorPair* accessors = AccessorPair::cast(callback_obj);
2127 if (Object* getter = accessors->getter()) {
2128 SetPropertyReference(js_obj, entry, descs->GetKey(i),
2129 getter, "get-%s");
2130 }
2131 if (Object* setter = accessors->setter()) {
2132 SetPropertyReference(js_obj, entry, descs->GetKey(i),
2133 setter, "set-%s");
2134 }
2135 }
2136 break;
2137 }
2138 case NORMAL: // only in slow mode
2139 case HANDLER: // only in lookup results, not in descriptors
2140 case INTERCEPTOR: // only in lookup results, not in descriptors
2141 case MAP_TRANSITION: // we do not care about transitions here...
2142 case ELEMENTS_TRANSITION:
2143 case CONSTANT_TRANSITION:
2144 case NULL_DESCRIPTOR: // ... and not about "holes"
2145 break;
2146 }
2147 }
2148 } else {
2149 StringDictionary* dictionary = js_obj->property_dictionary();
2150 int length = dictionary->Capacity();
2151 for (int i = 0; i < length; ++i) {
2152 Object* k = dictionary->KeyAt(i);
2153 if (dictionary->IsKey(k)) {
2154 Object* target = dictionary->ValueAt(i);
2155 SetPropertyReference(
2156 js_obj, entry, String::cast(k), target);
2157 // We assume that global objects can only have slow properties.
2158 if (target->IsJSGlobalPropertyCell()) {
2159 SetPropertyShortcutReference(js_obj,
2160 entry,
2161 String::cast(k),
2162 JSGlobalPropertyCell::cast(
2163 target)->value());
2164 }
2165 }
2166 }
2167 }
2168 }
2169
2170
ExtractElementReferences(JSObject * js_obj,HeapEntry * entry)2171 void V8HeapExplorer::ExtractElementReferences(JSObject* js_obj,
2172 HeapEntry* entry) {
2173 if (js_obj->HasFastElements()) {
2174 FixedArray* elements = FixedArray::cast(js_obj->elements());
2175 int length = js_obj->IsJSArray() ?
2176 Smi::cast(JSArray::cast(js_obj)->length())->value() :
2177 elements->length();
2178 for (int i = 0; i < length; ++i) {
2179 if (!elements->get(i)->IsTheHole()) {
2180 SetElementReference(js_obj, entry, i, elements->get(i));
2181 }
2182 }
2183 } else if (js_obj->HasDictionaryElements()) {
2184 SeededNumberDictionary* dictionary = js_obj->element_dictionary();
2185 int length = dictionary->Capacity();
2186 for (int i = 0; i < length; ++i) {
2187 Object* k = dictionary->KeyAt(i);
2188 if (dictionary->IsKey(k)) {
2189 ASSERT(k->IsNumber());
2190 uint32_t index = static_cast<uint32_t>(k->Number());
2191 SetElementReference(js_obj, entry, index, dictionary->ValueAt(i));
2192 }
2193 }
2194 }
2195 }
2196
2197
ExtractInternalReferences(JSObject * js_obj,HeapEntry * entry)2198 void V8HeapExplorer::ExtractInternalReferences(JSObject* js_obj,
2199 HeapEntry* entry) {
2200 int length = js_obj->GetInternalFieldCount();
2201 for (int i = 0; i < length; ++i) {
2202 Object* o = js_obj->GetInternalField(i);
2203 SetInternalReference(
2204 js_obj, entry, i, o, js_obj->GetInternalFieldOffset(i));
2205 }
2206 }
2207
2208
GetConstructorName(JSObject * object)2209 String* V8HeapExplorer::GetConstructorName(JSObject* object) {
2210 Heap* heap = object->GetHeap();
2211 if (object->IsJSFunction()) return heap->closure_symbol();
2212 String* constructor_name = object->constructor_name();
2213 if (constructor_name == heap->Object_symbol()) {
2214 // Look up an immediate "constructor" property, if it is a function,
2215 // return its name. This is for instances of binding objects, which
2216 // have prototype constructor type "Object".
2217 Object* constructor_prop = NULL;
2218 LookupResult result(heap->isolate());
2219 object->LocalLookupRealNamedProperty(heap->constructor_symbol(), &result);
2220 if (result.IsProperty()) {
2221 constructor_prop = result.GetLazyValue();
2222 }
2223 if (constructor_prop->IsJSFunction()) {
2224 Object* maybe_name = JSFunction::cast(constructor_prop)->shared()->name();
2225 if (maybe_name->IsString()) {
2226 String* name = String::cast(maybe_name);
2227 if (name->length() > 0) return name;
2228 }
2229 }
2230 }
2231 return object->constructor_name();
2232 }
2233
2234
GetEntry(Object * obj)2235 HeapEntry* V8HeapExplorer::GetEntry(Object* obj) {
2236 if (!obj->IsHeapObject()) return NULL;
2237 return filler_->FindOrAddEntry(obj, this);
2238 }
2239
2240
2241 class RootsReferencesExtractor : public ObjectVisitor {
2242 private:
2243 struct IndexTag {
IndexTagv8::internal::RootsReferencesExtractor::IndexTag2244 IndexTag(int index, VisitorSynchronization::SyncTag tag)
2245 : index(index), tag(tag) { }
2246 int index;
2247 VisitorSynchronization::SyncTag tag;
2248 };
2249
2250 public:
RootsReferencesExtractor()2251 RootsReferencesExtractor()
2252 : collecting_all_references_(false),
2253 previous_reference_count_(0) {
2254 }
2255
VisitPointers(Object ** start,Object ** end)2256 void VisitPointers(Object** start, Object** end) {
2257 if (collecting_all_references_) {
2258 for (Object** p = start; p < end; p++) all_references_.Add(*p);
2259 } else {
2260 for (Object** p = start; p < end; p++) strong_references_.Add(*p);
2261 }
2262 }
2263
SetCollectingAllReferences()2264 void SetCollectingAllReferences() { collecting_all_references_ = true; }
2265
FillReferences(V8HeapExplorer * explorer)2266 void FillReferences(V8HeapExplorer* explorer) {
2267 ASSERT(strong_references_.length() <= all_references_.length());
2268 for (int i = 0; i < reference_tags_.length(); ++i) {
2269 explorer->SetGcRootsReference(reference_tags_[i].tag);
2270 }
2271 int strong_index = 0, all_index = 0, tags_index = 0;
2272 while (all_index < all_references_.length()) {
2273 if (strong_index < strong_references_.length() &&
2274 strong_references_[strong_index] == all_references_[all_index]) {
2275 explorer->SetGcSubrootReference(reference_tags_[tags_index].tag,
2276 false,
2277 all_references_[all_index++]);
2278 ++strong_index;
2279 } else {
2280 explorer->SetGcSubrootReference(reference_tags_[tags_index].tag,
2281 true,
2282 all_references_[all_index++]);
2283 }
2284 if (reference_tags_[tags_index].index == all_index) ++tags_index;
2285 }
2286 }
2287
Synchronize(VisitorSynchronization::SyncTag tag)2288 void Synchronize(VisitorSynchronization::SyncTag tag) {
2289 if (collecting_all_references_ &&
2290 previous_reference_count_ != all_references_.length()) {
2291 previous_reference_count_ = all_references_.length();
2292 reference_tags_.Add(IndexTag(previous_reference_count_, tag));
2293 }
2294 }
2295
2296 private:
2297 bool collecting_all_references_;
2298 List<Object*> strong_references_;
2299 List<Object*> all_references_;
2300 int previous_reference_count_;
2301 List<IndexTag> reference_tags_;
2302 };
2303
2304
IterateAndExtractReferences(SnapshotFillerInterface * filler)2305 bool V8HeapExplorer::IterateAndExtractReferences(
2306 SnapshotFillerInterface* filler) {
2307 HeapIterator iterator(HeapIterator::kFilterUnreachable);
2308
2309 filler_ = filler;
2310 bool interrupted = false;
2311
2312 // Heap iteration with filtering must be finished in any case.
2313 for (HeapObject* obj = iterator.next();
2314 obj != NULL;
2315 obj = iterator.next(), progress_->ProgressStep()) {
2316 if (!interrupted) {
2317 ExtractReferences(obj);
2318 if (!progress_->ProgressReport(false)) interrupted = true;
2319 }
2320 }
2321 if (interrupted) {
2322 filler_ = NULL;
2323 return false;
2324 }
2325 SetRootGcRootsReference();
2326 RootsReferencesExtractor extractor;
2327 heap_->IterateRoots(&extractor, VISIT_ONLY_STRONG);
2328 extractor.SetCollectingAllReferences();
2329 heap_->IterateRoots(&extractor, VISIT_ALL);
2330 extractor.FillReferences(this);
2331 filler_ = NULL;
2332 return progress_->ProgressReport(false);
2333 }
2334
2335
IterateAndSetObjectNames(SnapshotFillerInterface * filler)2336 bool V8HeapExplorer::IterateAndSetObjectNames(SnapshotFillerInterface* filler) {
2337 HeapIterator iterator(HeapIterator::kFilterUnreachable);
2338 filler_ = filler;
2339 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
2340 SetObjectName(obj);
2341 }
2342 return true;
2343 }
2344
2345
SetObjectName(HeapObject * object)2346 void V8HeapExplorer::SetObjectName(HeapObject* object) {
2347 if (!object->IsJSObject() || object->IsJSRegExp() || object->IsJSFunction()) {
2348 return;
2349 }
2350 const char* name = collection_->names()->GetName(
2351 GetConstructorName(JSObject::cast(object)));
2352 if (object->IsJSGlobalObject()) {
2353 const char* tag = objects_tags_.GetTag(object);
2354 if (tag != NULL) {
2355 name = collection_->names()->GetFormatted("%s / %s", name, tag);
2356 }
2357 }
2358 GetEntry(object)->set_name(name);
2359 }
2360
2361
SetClosureReference(HeapObject * parent_obj,HeapEntry * parent_entry,String * reference_name,Object * child_obj)2362 void V8HeapExplorer::SetClosureReference(HeapObject* parent_obj,
2363 HeapEntry* parent_entry,
2364 String* reference_name,
2365 Object* child_obj) {
2366 HeapEntry* child_entry = GetEntry(child_obj);
2367 if (child_entry != NULL) {
2368 filler_->SetNamedReference(HeapGraphEdge::kContextVariable,
2369 parent_obj,
2370 parent_entry,
2371 collection_->names()->GetName(reference_name),
2372 child_obj,
2373 child_entry);
2374 }
2375 }
2376
2377
SetNativeBindReference(HeapObject * parent_obj,HeapEntry * parent_entry,const char * reference_name,Object * child_obj)2378 void V8HeapExplorer::SetNativeBindReference(HeapObject* parent_obj,
2379 HeapEntry* parent_entry,
2380 const char* reference_name,
2381 Object* child_obj) {
2382 HeapEntry* child_entry = GetEntry(child_obj);
2383 if (child_entry != NULL) {
2384 filler_->SetNamedReference(HeapGraphEdge::kShortcut,
2385 parent_obj,
2386 parent_entry,
2387 reference_name,
2388 child_obj,
2389 child_entry);
2390 }
2391 }
2392
2393
SetElementReference(HeapObject * parent_obj,HeapEntry * parent_entry,int index,Object * child_obj)2394 void V8HeapExplorer::SetElementReference(HeapObject* parent_obj,
2395 HeapEntry* parent_entry,
2396 int index,
2397 Object* child_obj) {
2398 HeapEntry* child_entry = GetEntry(child_obj);
2399 if (child_entry != NULL) {
2400 filler_->SetIndexedReference(HeapGraphEdge::kElement,
2401 parent_obj,
2402 parent_entry,
2403 index,
2404 child_obj,
2405 child_entry);
2406 }
2407 }
2408
2409
SetInternalReference(HeapObject * parent_obj,HeapEntry * parent_entry,const char * reference_name,Object * child_obj,int field_offset)2410 void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
2411 HeapEntry* parent_entry,
2412 const char* reference_name,
2413 Object* child_obj,
2414 int field_offset) {
2415 HeapEntry* child_entry = GetEntry(child_obj);
2416 if (child_entry != NULL) {
2417 filler_->SetNamedReference(HeapGraphEdge::kInternal,
2418 parent_obj,
2419 parent_entry,
2420 reference_name,
2421 child_obj,
2422 child_entry);
2423 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
2424 }
2425 }
2426
2427
SetInternalReference(HeapObject * parent_obj,HeapEntry * parent_entry,int index,Object * child_obj,int field_offset)2428 void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
2429 HeapEntry* parent_entry,
2430 int index,
2431 Object* child_obj,
2432 int field_offset) {
2433 HeapEntry* child_entry = GetEntry(child_obj);
2434 if (child_entry != NULL) {
2435 filler_->SetNamedReference(HeapGraphEdge::kInternal,
2436 parent_obj,
2437 parent_entry,
2438 collection_->names()->GetName(index),
2439 child_obj,
2440 child_entry);
2441 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
2442 }
2443 }
2444
2445
SetHiddenReference(HeapObject * parent_obj,HeapEntry * parent_entry,int index,Object * child_obj)2446 void V8HeapExplorer::SetHiddenReference(HeapObject* parent_obj,
2447 HeapEntry* parent_entry,
2448 int index,
2449 Object* child_obj) {
2450 HeapEntry* child_entry = GetEntry(child_obj);
2451 if (child_entry != NULL) {
2452 filler_->SetIndexedReference(HeapGraphEdge::kHidden,
2453 parent_obj,
2454 parent_entry,
2455 index,
2456 child_obj,
2457 child_entry);
2458 }
2459 }
2460
2461
SetWeakReference(HeapObject * parent_obj,HeapEntry * parent_entry,int index,Object * child_obj,int field_offset)2462 void V8HeapExplorer::SetWeakReference(HeapObject* parent_obj,
2463 HeapEntry* parent_entry,
2464 int index,
2465 Object* child_obj,
2466 int field_offset) {
2467 HeapEntry* child_entry = GetEntry(child_obj);
2468 if (child_entry != NULL) {
2469 filler_->SetIndexedReference(HeapGraphEdge::kWeak,
2470 parent_obj,
2471 parent_entry,
2472 index,
2473 child_obj,
2474 child_entry);
2475 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
2476 }
2477 }
2478
2479
SetPropertyReference(HeapObject * parent_obj,HeapEntry * parent_entry,String * reference_name,Object * child_obj,const char * name_format_string,int field_offset)2480 void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj,
2481 HeapEntry* parent_entry,
2482 String* reference_name,
2483 Object* child_obj,
2484 const char* name_format_string,
2485 int field_offset) {
2486 HeapEntry* child_entry = GetEntry(child_obj);
2487 if (child_entry != NULL) {
2488 HeapGraphEdge::Type type = reference_name->length() > 0 ?
2489 HeapGraphEdge::kProperty : HeapGraphEdge::kInternal;
2490 const char* name = name_format_string != NULL ?
2491 collection_->names()->GetFormatted(
2492 name_format_string,
2493 *reference_name->ToCString(DISALLOW_NULLS,
2494 ROBUST_STRING_TRAVERSAL)) :
2495 collection_->names()->GetName(reference_name);
2496
2497 filler_->SetNamedReference(type,
2498 parent_obj,
2499 parent_entry,
2500 name,
2501 child_obj,
2502 child_entry);
2503 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
2504 }
2505 }
2506
2507
SetPropertyShortcutReference(HeapObject * parent_obj,HeapEntry * parent_entry,String * reference_name,Object * child_obj)2508 void V8HeapExplorer::SetPropertyShortcutReference(HeapObject* parent_obj,
2509 HeapEntry* parent_entry,
2510 String* reference_name,
2511 Object* child_obj) {
2512 HeapEntry* child_entry = GetEntry(child_obj);
2513 if (child_entry != NULL) {
2514 filler_->SetNamedReference(HeapGraphEdge::kShortcut,
2515 parent_obj,
2516 parent_entry,
2517 collection_->names()->GetName(reference_name),
2518 child_obj,
2519 child_entry);
2520 }
2521 }
2522
2523
SetRootGcRootsReference()2524 void V8HeapExplorer::SetRootGcRootsReference() {
2525 filler_->SetIndexedAutoIndexReference(
2526 HeapGraphEdge::kElement,
2527 kInternalRootObject, snapshot_->root(),
2528 kGcRootsObject, snapshot_->gc_roots());
2529 }
2530
2531
SetRootShortcutReference(Object * child_obj)2532 void V8HeapExplorer::SetRootShortcutReference(Object* child_obj) {
2533 HeapEntry* child_entry = GetEntry(child_obj);
2534 ASSERT(child_entry != NULL);
2535 filler_->SetNamedAutoIndexReference(
2536 HeapGraphEdge::kShortcut,
2537 kInternalRootObject, snapshot_->root(),
2538 child_obj, child_entry);
2539 }
2540
2541
SetGcRootsReference(VisitorSynchronization::SyncTag tag)2542 void V8HeapExplorer::SetGcRootsReference(VisitorSynchronization::SyncTag tag) {
2543 filler_->SetIndexedAutoIndexReference(
2544 HeapGraphEdge::kElement,
2545 kGcRootsObject, snapshot_->gc_roots(),
2546 GetNthGcSubrootObject(tag), snapshot_->gc_subroot(tag));
2547 }
2548
2549
SetGcSubrootReference(VisitorSynchronization::SyncTag tag,bool is_weak,Object * child_obj)2550 void V8HeapExplorer::SetGcSubrootReference(
2551 VisitorSynchronization::SyncTag tag, bool is_weak, Object* child_obj) {
2552 HeapEntry* child_entry = GetEntry(child_obj);
2553 if (child_entry != NULL) {
2554 filler_->SetIndexedAutoIndexReference(
2555 is_weak ? HeapGraphEdge::kWeak : HeapGraphEdge::kElement,
2556 GetNthGcSubrootObject(tag), snapshot_->gc_subroot(tag),
2557 child_obj, child_entry);
2558 }
2559 }
2560
2561
TagObject(Object * obj,const char * tag)2562 void V8HeapExplorer::TagObject(Object* obj, const char* tag) {
2563 if (obj->IsHeapObject() &&
2564 !obj->IsOddball() &&
2565 obj != heap_->raw_unchecked_empty_byte_array() &&
2566 obj != heap_->raw_unchecked_empty_fixed_array() &&
2567 obj != heap_->raw_unchecked_empty_descriptor_array()) {
2568 objects_tags_.SetTag(obj, tag);
2569 }
2570 }
2571
2572
2573 class GlobalObjectsEnumerator : public ObjectVisitor {
2574 public:
VisitPointers(Object ** start,Object ** end)2575 virtual void VisitPointers(Object** start, Object** end) {
2576 for (Object** p = start; p < end; p++) {
2577 if ((*p)->IsGlobalContext()) {
2578 Context* context = Context::cast(*p);
2579 JSObject* proxy = context->global_proxy();
2580 if (proxy->IsJSGlobalProxy()) {
2581 Object* global = proxy->map()->prototype();
2582 if (global->IsJSGlobalObject()) {
2583 objects_.Add(Handle<JSGlobalObject>(JSGlobalObject::cast(global)));
2584 }
2585 }
2586 }
2587 }
2588 }
count()2589 int count() { return objects_.length(); }
at(int i)2590 Handle<JSGlobalObject>& at(int i) { return objects_[i]; }
2591
2592 private:
2593 List<Handle<JSGlobalObject> > objects_;
2594 };
2595
2596
2597 // Modifies heap. Must not be run during heap traversal.
TagGlobalObjects()2598 void V8HeapExplorer::TagGlobalObjects() {
2599 HandleScope scope;
2600 Isolate* isolate = Isolate::Current();
2601 GlobalObjectsEnumerator enumerator;
2602 isolate->global_handles()->IterateAllRoots(&enumerator);
2603 Handle<String> document_string =
2604 isolate->factory()->NewStringFromAscii(CStrVector("document"));
2605 Handle<String> url_string =
2606 isolate->factory()->NewStringFromAscii(CStrVector("URL"));
2607 const char** urls = NewArray<const char*>(enumerator.count());
2608 for (int i = 0, l = enumerator.count(); i < l; ++i) {
2609 urls[i] = NULL;
2610 HandleScope scope;
2611 Handle<JSGlobalObject> global_obj = enumerator.at(i);
2612 Object* obj_document;
2613 if (global_obj->GetProperty(*document_string)->ToObject(&obj_document) &&
2614 obj_document->IsJSObject()) {
2615 JSObject* document = JSObject::cast(obj_document);
2616 Object* obj_url;
2617 if (document->GetProperty(*url_string)->ToObject(&obj_url) &&
2618 obj_url->IsString()) {
2619 urls[i] = collection_->names()->GetName(String::cast(obj_url));
2620 }
2621 }
2622 }
2623
2624 AssertNoAllocation no_allocation;
2625 for (int i = 0, l = enumerator.count(); i < l; ++i) {
2626 objects_tags_.SetTag(*enumerator.at(i), urls[i]);
2627 }
2628
2629 DeleteArray(urls);
2630 }
2631
2632
2633 class GlobalHandlesExtractor : public ObjectVisitor {
2634 public:
GlobalHandlesExtractor(NativeObjectsExplorer * explorer)2635 explicit GlobalHandlesExtractor(NativeObjectsExplorer* explorer)
2636 : explorer_(explorer) {}
~GlobalHandlesExtractor()2637 virtual ~GlobalHandlesExtractor() {}
VisitPointers(Object ** start,Object ** end)2638 virtual void VisitPointers(Object** start, Object** end) {
2639 UNREACHABLE();
2640 }
VisitEmbedderReference(Object ** p,uint16_t class_id)2641 virtual void VisitEmbedderReference(Object** p, uint16_t class_id) {
2642 explorer_->VisitSubtreeWrapper(p, class_id);
2643 }
2644 private:
2645 NativeObjectsExplorer* explorer_;
2646 };
2647
2648
2649 class BasicHeapEntriesAllocator : public HeapEntriesAllocator {
2650 public:
BasicHeapEntriesAllocator(HeapSnapshot * snapshot,HeapEntry::Type entries_type)2651 BasicHeapEntriesAllocator(
2652 HeapSnapshot* snapshot,
2653 HeapEntry::Type entries_type)
2654 : snapshot_(snapshot),
2655 collection_(snapshot_->collection()),
2656 entries_type_(entries_type) {
2657 }
2658 virtual HeapEntry* AllocateEntry(
2659 HeapThing ptr, int children_count, int retainers_count);
2660 private:
2661 HeapSnapshot* snapshot_;
2662 HeapSnapshotsCollection* collection_;
2663 HeapEntry::Type entries_type_;
2664 };
2665
2666
AllocateEntry(HeapThing ptr,int children_count,int retainers_count)2667 HeapEntry* BasicHeapEntriesAllocator::AllocateEntry(
2668 HeapThing ptr, int children_count, int retainers_count) {
2669 v8::RetainedObjectInfo* info = reinterpret_cast<v8::RetainedObjectInfo*>(ptr);
2670 intptr_t elements = info->GetElementCount();
2671 intptr_t size = info->GetSizeInBytes();
2672 return snapshot_->AddEntry(
2673 entries_type_,
2674 elements != -1 ?
2675 collection_->names()->GetFormatted(
2676 "%s / %" V8_PTR_PREFIX "d entries",
2677 info->GetLabel(),
2678 info->GetElementCount()) :
2679 collection_->names()->GetCopy(info->GetLabel()),
2680 HeapObjectsMap::GenerateId(info),
2681 size != -1 ? static_cast<int>(size) : 0,
2682 children_count,
2683 retainers_count);
2684 }
2685
2686
NativeObjectsExplorer(HeapSnapshot * snapshot,SnapshottingProgressReportingInterface * progress)2687 NativeObjectsExplorer::NativeObjectsExplorer(
2688 HeapSnapshot* snapshot, SnapshottingProgressReportingInterface* progress)
2689 : snapshot_(snapshot),
2690 collection_(snapshot_->collection()),
2691 progress_(progress),
2692 embedder_queried_(false),
2693 objects_by_info_(RetainedInfosMatch),
2694 native_groups_(StringsMatch),
2695 filler_(NULL) {
2696 synthetic_entries_allocator_ =
2697 new BasicHeapEntriesAllocator(snapshot, HeapEntry::kSynthetic);
2698 native_entries_allocator_ =
2699 new BasicHeapEntriesAllocator(snapshot, HeapEntry::kNative);
2700 }
2701
2702
~NativeObjectsExplorer()2703 NativeObjectsExplorer::~NativeObjectsExplorer() {
2704 for (HashMap::Entry* p = objects_by_info_.Start();
2705 p != NULL;
2706 p = objects_by_info_.Next(p)) {
2707 v8::RetainedObjectInfo* info =
2708 reinterpret_cast<v8::RetainedObjectInfo*>(p->key);
2709 info->Dispose();
2710 List<HeapObject*>* objects =
2711 reinterpret_cast<List<HeapObject*>* >(p->value);
2712 delete objects;
2713 }
2714 for (HashMap::Entry* p = native_groups_.Start();
2715 p != NULL;
2716 p = native_groups_.Next(p)) {
2717 v8::RetainedObjectInfo* info =
2718 reinterpret_cast<v8::RetainedObjectInfo*>(p->value);
2719 info->Dispose();
2720 }
2721 delete synthetic_entries_allocator_;
2722 delete native_entries_allocator_;
2723 }
2724
2725
EstimateObjectsCount()2726 int NativeObjectsExplorer::EstimateObjectsCount() {
2727 FillRetainedObjects();
2728 return objects_by_info_.occupancy();
2729 }
2730
2731
FillRetainedObjects()2732 void NativeObjectsExplorer::FillRetainedObjects() {
2733 if (embedder_queried_) return;
2734 Isolate* isolate = Isolate::Current();
2735 // Record objects that are joined into ObjectGroups.
2736 isolate->heap()->CallGlobalGCPrologueCallback();
2737 List<ObjectGroup*>* groups = isolate->global_handles()->object_groups();
2738 for (int i = 0; i < groups->length(); ++i) {
2739 ObjectGroup* group = groups->at(i);
2740 if (group->info_ == NULL) continue;
2741 List<HeapObject*>* list = GetListMaybeDisposeInfo(group->info_);
2742 for (size_t j = 0; j < group->length_; ++j) {
2743 HeapObject* obj = HeapObject::cast(*group->objects_[j]);
2744 list->Add(obj);
2745 in_groups_.Insert(obj);
2746 }
2747 group->info_ = NULL; // Acquire info object ownership.
2748 }
2749 isolate->global_handles()->RemoveObjectGroups();
2750 isolate->heap()->CallGlobalGCEpilogueCallback();
2751 // Record objects that are not in ObjectGroups, but have class ID.
2752 GlobalHandlesExtractor extractor(this);
2753 isolate->global_handles()->IterateAllRootsWithClassIds(&extractor);
2754 embedder_queried_ = true;
2755 }
2756
FillImplicitReferences()2757 void NativeObjectsExplorer::FillImplicitReferences() {
2758 Isolate* isolate = Isolate::Current();
2759 List<ImplicitRefGroup*>* groups =
2760 isolate->global_handles()->implicit_ref_groups();
2761 for (int i = 0; i < groups->length(); ++i) {
2762 ImplicitRefGroup* group = groups->at(i);
2763 HeapObject* parent = *group->parent_;
2764 HeapEntry* parent_entry =
2765 filler_->FindOrAddEntry(parent, native_entries_allocator_);
2766 ASSERT(parent_entry != NULL);
2767 Object*** children = group->children_;
2768 for (size_t j = 0; j < group->length_; ++j) {
2769 Object* child = *children[j];
2770 HeapEntry* child_entry =
2771 filler_->FindOrAddEntry(child, native_entries_allocator_);
2772 filler_->SetNamedReference(
2773 HeapGraphEdge::kInternal,
2774 parent, parent_entry,
2775 "native",
2776 child, child_entry);
2777 }
2778 }
2779 }
2780
GetListMaybeDisposeInfo(v8::RetainedObjectInfo * info)2781 List<HeapObject*>* NativeObjectsExplorer::GetListMaybeDisposeInfo(
2782 v8::RetainedObjectInfo* info) {
2783 HashMap::Entry* entry =
2784 objects_by_info_.Lookup(info, InfoHash(info), true);
2785 if (entry->value != NULL) {
2786 info->Dispose();
2787 } else {
2788 entry->value = new List<HeapObject*>(4);
2789 }
2790 return reinterpret_cast<List<HeapObject*>* >(entry->value);
2791 }
2792
2793
IterateAndExtractReferences(SnapshotFillerInterface * filler)2794 bool NativeObjectsExplorer::IterateAndExtractReferences(
2795 SnapshotFillerInterface* filler) {
2796 filler_ = filler;
2797 FillRetainedObjects();
2798 FillImplicitReferences();
2799 if (EstimateObjectsCount() > 0) {
2800 for (HashMap::Entry* p = objects_by_info_.Start();
2801 p != NULL;
2802 p = objects_by_info_.Next(p)) {
2803 v8::RetainedObjectInfo* info =
2804 reinterpret_cast<v8::RetainedObjectInfo*>(p->key);
2805 SetNativeRootReference(info);
2806 List<HeapObject*>* objects =
2807 reinterpret_cast<List<HeapObject*>* >(p->value);
2808 for (int i = 0; i < objects->length(); ++i) {
2809 SetWrapperNativeReferences(objects->at(i), info);
2810 }
2811 }
2812 SetRootNativeRootsReference();
2813 }
2814 filler_ = NULL;
2815 return true;
2816 }
2817
2818
2819 class NativeGroupRetainedObjectInfo : public v8::RetainedObjectInfo {
2820 public:
NativeGroupRetainedObjectInfo(const char * label)2821 explicit NativeGroupRetainedObjectInfo(const char* label)
2822 : disposed_(false),
2823 hash_(reinterpret_cast<intptr_t>(label)),
2824 label_(label) {
2825 }
2826
~NativeGroupRetainedObjectInfo()2827 virtual ~NativeGroupRetainedObjectInfo() {}
Dispose()2828 virtual void Dispose() {
2829 CHECK(!disposed_);
2830 disposed_ = true;
2831 delete this;
2832 }
IsEquivalent(RetainedObjectInfo * other)2833 virtual bool IsEquivalent(RetainedObjectInfo* other) {
2834 return hash_ == other->GetHash() && !strcmp(label_, other->GetLabel());
2835 }
GetHash()2836 virtual intptr_t GetHash() { return hash_; }
GetLabel()2837 virtual const char* GetLabel() { return label_; }
2838
2839 private:
2840 bool disposed_;
2841 intptr_t hash_;
2842 const char* label_;
2843 };
2844
2845
FindOrAddGroupInfo(const char * label)2846 NativeGroupRetainedObjectInfo* NativeObjectsExplorer::FindOrAddGroupInfo(
2847 const char* label) {
2848 const char* label_copy = collection_->names()->GetCopy(label);
2849 uint32_t hash = HashSequentialString(label_copy,
2850 static_cast<int>(strlen(label_copy)),
2851 HEAP->HashSeed());
2852 HashMap::Entry* entry = native_groups_.Lookup(const_cast<char*>(label_copy),
2853 hash, true);
2854 if (entry->value == NULL)
2855 entry->value = new NativeGroupRetainedObjectInfo(label);
2856 return static_cast<NativeGroupRetainedObjectInfo*>(entry->value);
2857 }
2858
2859
SetNativeRootReference(v8::RetainedObjectInfo * info)2860 void NativeObjectsExplorer::SetNativeRootReference(
2861 v8::RetainedObjectInfo* info) {
2862 HeapEntry* child_entry =
2863 filler_->FindOrAddEntry(info, native_entries_allocator_);
2864 ASSERT(child_entry != NULL);
2865 NativeGroupRetainedObjectInfo* group_info =
2866 FindOrAddGroupInfo(info->GetGroupLabel());
2867 HeapEntry* group_entry =
2868 filler_->FindOrAddEntry(group_info, synthetic_entries_allocator_);
2869 filler_->SetNamedAutoIndexReference(
2870 HeapGraphEdge::kInternal,
2871 group_info, group_entry,
2872 info, child_entry);
2873 }
2874
2875
SetWrapperNativeReferences(HeapObject * wrapper,v8::RetainedObjectInfo * info)2876 void NativeObjectsExplorer::SetWrapperNativeReferences(
2877 HeapObject* wrapper, v8::RetainedObjectInfo* info) {
2878 HeapEntry* wrapper_entry = filler_->FindEntry(wrapper);
2879 ASSERT(wrapper_entry != NULL);
2880 HeapEntry* info_entry =
2881 filler_->FindOrAddEntry(info, native_entries_allocator_);
2882 ASSERT(info_entry != NULL);
2883 filler_->SetNamedReference(HeapGraphEdge::kInternal,
2884 wrapper, wrapper_entry,
2885 "native",
2886 info, info_entry);
2887 filler_->SetIndexedAutoIndexReference(HeapGraphEdge::kElement,
2888 info, info_entry,
2889 wrapper, wrapper_entry);
2890 }
2891
2892
SetRootNativeRootsReference()2893 void NativeObjectsExplorer::SetRootNativeRootsReference() {
2894 for (HashMap::Entry* entry = native_groups_.Start();
2895 entry;
2896 entry = native_groups_.Next(entry)) {
2897 NativeGroupRetainedObjectInfo* group_info =
2898 static_cast<NativeGroupRetainedObjectInfo*>(entry->value);
2899 HeapEntry* group_entry =
2900 filler_->FindOrAddEntry(group_info, native_entries_allocator_);
2901 ASSERT(group_entry != NULL);
2902 filler_->SetIndexedAutoIndexReference(
2903 HeapGraphEdge::kElement,
2904 V8HeapExplorer::kInternalRootObject, snapshot_->root(),
2905 group_info, group_entry);
2906 }
2907 }
2908
2909
VisitSubtreeWrapper(Object ** p,uint16_t class_id)2910 void NativeObjectsExplorer::VisitSubtreeWrapper(Object** p, uint16_t class_id) {
2911 if (in_groups_.Contains(*p)) return;
2912 Isolate* isolate = Isolate::Current();
2913 v8::RetainedObjectInfo* info =
2914 isolate->heap_profiler()->ExecuteWrapperClassCallback(class_id, p);
2915 if (info == NULL) return;
2916 GetListMaybeDisposeInfo(info)->Add(HeapObject::cast(*p));
2917 }
2918
2919
2920 class SnapshotCounter : public SnapshotFillerInterface {
2921 public:
SnapshotCounter(HeapEntriesMap * entries)2922 explicit SnapshotCounter(HeapEntriesMap* entries) : entries_(entries) { }
AddEntry(HeapThing ptr,HeapEntriesAllocator * allocator)2923 HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
2924 entries_->Pair(ptr, allocator, HeapEntriesMap::kHeapEntryPlaceholder);
2925 return HeapEntriesMap::kHeapEntryPlaceholder;
2926 }
FindEntry(HeapThing ptr)2927 HeapEntry* FindEntry(HeapThing ptr) {
2928 return entries_->Map(ptr);
2929 }
FindOrAddEntry(HeapThing ptr,HeapEntriesAllocator * allocator)2930 HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
2931 HeapEntry* entry = FindEntry(ptr);
2932 return entry != NULL ? entry : AddEntry(ptr, allocator);
2933 }
SetIndexedReference(HeapGraphEdge::Type,HeapThing parent_ptr,HeapEntry *,int,HeapThing child_ptr,HeapEntry *)2934 void SetIndexedReference(HeapGraphEdge::Type,
2935 HeapThing parent_ptr,
2936 HeapEntry*,
2937 int,
2938 HeapThing child_ptr,
2939 HeapEntry*) {
2940 entries_->CountReference(parent_ptr, child_ptr);
2941 }
SetIndexedAutoIndexReference(HeapGraphEdge::Type,HeapThing parent_ptr,HeapEntry *,HeapThing child_ptr,HeapEntry *)2942 void SetIndexedAutoIndexReference(HeapGraphEdge::Type,
2943 HeapThing parent_ptr,
2944 HeapEntry*,
2945 HeapThing child_ptr,
2946 HeapEntry*) {
2947 entries_->CountReference(parent_ptr, child_ptr);
2948 }
SetNamedReference(HeapGraphEdge::Type,HeapThing parent_ptr,HeapEntry *,const char *,HeapThing child_ptr,HeapEntry *)2949 void SetNamedReference(HeapGraphEdge::Type,
2950 HeapThing parent_ptr,
2951 HeapEntry*,
2952 const char*,
2953 HeapThing child_ptr,
2954 HeapEntry*) {
2955 entries_->CountReference(parent_ptr, child_ptr);
2956 }
SetNamedAutoIndexReference(HeapGraphEdge::Type,HeapThing parent_ptr,HeapEntry *,HeapThing child_ptr,HeapEntry *)2957 void SetNamedAutoIndexReference(HeapGraphEdge::Type,
2958 HeapThing parent_ptr,
2959 HeapEntry*,
2960 HeapThing child_ptr,
2961 HeapEntry*) {
2962 entries_->CountReference(parent_ptr, child_ptr);
2963 }
2964
2965 private:
2966 HeapEntriesMap* entries_;
2967 };
2968
2969
2970 class SnapshotFiller : public SnapshotFillerInterface {
2971 public:
SnapshotFiller(HeapSnapshot * snapshot,HeapEntriesMap * entries)2972 explicit SnapshotFiller(HeapSnapshot* snapshot, HeapEntriesMap* entries)
2973 : snapshot_(snapshot),
2974 collection_(snapshot->collection()),
2975 entries_(entries) { }
AddEntry(HeapThing ptr,HeapEntriesAllocator * allocator)2976 HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
2977 UNREACHABLE();
2978 return NULL;
2979 }
FindEntry(HeapThing ptr)2980 HeapEntry* FindEntry(HeapThing ptr) {
2981 return entries_->Map(ptr);
2982 }
FindOrAddEntry(HeapThing ptr,HeapEntriesAllocator * allocator)2983 HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
2984 HeapEntry* entry = FindEntry(ptr);
2985 return entry != NULL ? entry : AddEntry(ptr, allocator);
2986 }
SetIndexedReference(HeapGraphEdge::Type type,HeapThing parent_ptr,HeapEntry * parent_entry,int index,HeapThing child_ptr,HeapEntry * child_entry)2987 void SetIndexedReference(HeapGraphEdge::Type type,
2988 HeapThing parent_ptr,
2989 HeapEntry* parent_entry,
2990 int index,
2991 HeapThing child_ptr,
2992 HeapEntry* child_entry) {
2993 int child_index, retainer_index;
2994 entries_->CountReference(
2995 parent_ptr, child_ptr, &child_index, &retainer_index);
2996 parent_entry->SetIndexedReference(
2997 type, child_index, index, child_entry, retainer_index);
2998 }
SetIndexedAutoIndexReference(HeapGraphEdge::Type type,HeapThing parent_ptr,HeapEntry * parent_entry,HeapThing child_ptr,HeapEntry * child_entry)2999 void SetIndexedAutoIndexReference(HeapGraphEdge::Type type,
3000 HeapThing parent_ptr,
3001 HeapEntry* parent_entry,
3002 HeapThing child_ptr,
3003 HeapEntry* child_entry) {
3004 int child_index, retainer_index;
3005 entries_->CountReference(
3006 parent_ptr, child_ptr, &child_index, &retainer_index);
3007 parent_entry->SetIndexedReference(
3008 type, child_index, child_index + 1, child_entry, retainer_index);
3009 }
SetNamedReference(HeapGraphEdge::Type type,HeapThing parent_ptr,HeapEntry * parent_entry,const char * reference_name,HeapThing child_ptr,HeapEntry * child_entry)3010 void SetNamedReference(HeapGraphEdge::Type type,
3011 HeapThing parent_ptr,
3012 HeapEntry* parent_entry,
3013 const char* reference_name,
3014 HeapThing child_ptr,
3015 HeapEntry* child_entry) {
3016 int child_index, retainer_index;
3017 entries_->CountReference(
3018 parent_ptr, child_ptr, &child_index, &retainer_index);
3019 parent_entry->SetNamedReference(
3020 type, child_index, reference_name, child_entry, retainer_index);
3021 }
SetNamedAutoIndexReference(HeapGraphEdge::Type type,HeapThing parent_ptr,HeapEntry * parent_entry,HeapThing child_ptr,HeapEntry * child_entry)3022 void SetNamedAutoIndexReference(HeapGraphEdge::Type type,
3023 HeapThing parent_ptr,
3024 HeapEntry* parent_entry,
3025 HeapThing child_ptr,
3026 HeapEntry* child_entry) {
3027 int child_index, retainer_index;
3028 entries_->CountReference(
3029 parent_ptr, child_ptr, &child_index, &retainer_index);
3030 parent_entry->SetNamedReference(type,
3031 child_index,
3032 collection_->names()->GetName(child_index + 1),
3033 child_entry,
3034 retainer_index);
3035 }
3036
3037 private:
3038 HeapSnapshot* snapshot_;
3039 HeapSnapshotsCollection* collection_;
3040 HeapEntriesMap* entries_;
3041 };
3042
3043
HeapSnapshotGenerator(HeapSnapshot * snapshot,v8::ActivityControl * control)3044 HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot,
3045 v8::ActivityControl* control)
3046 : snapshot_(snapshot),
3047 control_(control),
3048 v8_heap_explorer_(snapshot_, this),
3049 dom_explorer_(snapshot_, this) {
3050 }
3051
3052
GenerateSnapshot()3053 bool HeapSnapshotGenerator::GenerateSnapshot() {
3054 v8_heap_explorer_.TagGlobalObjects();
3055
3056 // TODO(1562) Profiler assumes that any object that is in the heap after
3057 // full GC is reachable from the root when computing dominators.
3058 // This is not true for weakly reachable objects.
3059 // As a temporary solution we call GC twice.
3060 Isolate::Current()->heap()->CollectAllGarbage(
3061 Heap::kMakeHeapIterableMask,
3062 "HeapSnapshotGenerator::GenerateSnapshot");
3063 Isolate::Current()->heap()->CollectAllGarbage(
3064 Heap::kMakeHeapIterableMask,
3065 "HeapSnapshotGenerator::GenerateSnapshot");
3066
3067 #ifdef DEBUG
3068 Heap* debug_heap = Isolate::Current()->heap();
3069 ASSERT(!debug_heap->old_data_space()->was_swept_conservatively());
3070 ASSERT(!debug_heap->old_pointer_space()->was_swept_conservatively());
3071 ASSERT(!debug_heap->code_space()->was_swept_conservatively());
3072 ASSERT(!debug_heap->cell_space()->was_swept_conservatively());
3073 ASSERT(!debug_heap->map_space()->was_swept_conservatively());
3074 #endif
3075
3076 // The following code uses heap iterators, so we want the heap to be
3077 // stable. It should follow TagGlobalObjects as that can allocate.
3078 AssertNoAllocation no_alloc;
3079
3080 #ifdef DEBUG
3081 debug_heap->Verify();
3082 #endif
3083
3084 SetProgressTotal(2); // 2 passes.
3085
3086 #ifdef DEBUG
3087 debug_heap->Verify();
3088 #endif
3089
3090 // Pass 1. Iterate heap contents to count entries and references.
3091 if (!CountEntriesAndReferences()) return false;
3092
3093 #ifdef DEBUG
3094 debug_heap->Verify();
3095 #endif
3096
3097 // Allocate memory for entries and references.
3098 snapshot_->AllocateEntries(entries_.entries_count(),
3099 entries_.total_children_count(),
3100 entries_.total_retainers_count());
3101
3102 // Allocate heap objects to entries hash map.
3103 entries_.AllocateEntries();
3104
3105 // Pass 2. Fill references.
3106 if (!FillReferences()) return false;
3107
3108 if (!SetEntriesDominators()) return false;
3109 if (!CalculateRetainedSizes()) return false;
3110
3111 progress_counter_ = progress_total_;
3112 if (!ProgressReport(true)) return false;
3113 return true;
3114 }
3115
3116
ProgressStep()3117 void HeapSnapshotGenerator::ProgressStep() {
3118 ++progress_counter_;
3119 }
3120
3121
ProgressReport(bool force)3122 bool HeapSnapshotGenerator::ProgressReport(bool force) {
3123 const int kProgressReportGranularity = 10000;
3124 if (control_ != NULL
3125 && (force || progress_counter_ % kProgressReportGranularity == 0)) {
3126 return
3127 control_->ReportProgressValue(progress_counter_, progress_total_) ==
3128 v8::ActivityControl::kContinue;
3129 }
3130 return true;
3131 }
3132
3133
SetProgressTotal(int iterations_count)3134 void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) {
3135 if (control_ == NULL) return;
3136 HeapIterator iterator(HeapIterator::kFilterUnreachable);
3137 progress_total_ = (
3138 v8_heap_explorer_.EstimateObjectsCount(&iterator) +
3139 dom_explorer_.EstimateObjectsCount()) * iterations_count;
3140 progress_counter_ = 0;
3141 }
3142
3143
CountEntriesAndReferences()3144 bool HeapSnapshotGenerator::CountEntriesAndReferences() {
3145 SnapshotCounter counter(&entries_);
3146 v8_heap_explorer_.AddRootEntries(&counter);
3147 return v8_heap_explorer_.IterateAndExtractReferences(&counter)
3148 && dom_explorer_.IterateAndExtractReferences(&counter);
3149 }
3150
3151
FillReferences()3152 bool HeapSnapshotGenerator::FillReferences() {
3153 SnapshotFiller filler(snapshot_, &entries_);
3154 // IterateAndExtractReferences cannot set object names because
3155 // it makes call to JSObject::LocalLookupRealNamedProperty which
3156 // in turn may relocate objects in property maps thus changing the heap
3157 // layout and affecting retainer counts. This is not acceptable because
3158 // number of retainers must not change between count and fill passes.
3159 // To avoid this there's a separate postpass that set object names.
3160 return v8_heap_explorer_.IterateAndExtractReferences(&filler)
3161 && dom_explorer_.IterateAndExtractReferences(&filler)
3162 && v8_heap_explorer_.IterateAndSetObjectNames(&filler);
3163 }
3164
3165
FillReversePostorderIndexes(Vector<HeapEntry * > * entries)3166 void HeapSnapshotGenerator::FillReversePostorderIndexes(
3167 Vector<HeapEntry*>* entries) {
3168 snapshot_->ClearPaint();
3169 int current_entry = 0;
3170 List<HeapEntry*> nodes_to_visit;
3171 nodes_to_visit.Add(snapshot_->root());
3172 snapshot_->root()->paint();
3173 while (!nodes_to_visit.is_empty()) {
3174 HeapEntry* entry = nodes_to_visit.last();
3175 Vector<HeapGraphEdge> children = entry->children();
3176 bool has_new_edges = false;
3177 for (int i = 0; i < children.length(); ++i) {
3178 if (children[i].type() == HeapGraphEdge::kShortcut) continue;
3179 HeapEntry* child = children[i].to();
3180 if (!child->painted()) {
3181 nodes_to_visit.Add(child);
3182 child->paint();
3183 has_new_edges = true;
3184 }
3185 }
3186 if (!has_new_edges) {
3187 entry->set_ordered_index(current_entry);
3188 (*entries)[current_entry++] = entry;
3189 nodes_to_visit.RemoveLast();
3190 }
3191 }
3192 ASSERT_EQ(current_entry, entries->length());
3193 }
3194
3195
Intersect(int i1,int i2,const Vector<int> & dominators)3196 static int Intersect(int i1, int i2, const Vector<int>& dominators) {
3197 int finger1 = i1, finger2 = i2;
3198 while (finger1 != finger2) {
3199 while (finger1 < finger2) finger1 = dominators[finger1];
3200 while (finger2 < finger1) finger2 = dominators[finger2];
3201 }
3202 return finger1;
3203 }
3204
3205
3206 // The algorithm is based on the article:
3207 // K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm"
3208 // Softw. Pract. Exper. 4 (2001), pp. 1-10.
BuildDominatorTree(const Vector<HeapEntry * > & entries,Vector<int> * dominators)3209 bool HeapSnapshotGenerator::BuildDominatorTree(
3210 const Vector<HeapEntry*>& entries,
3211 Vector<int>* dominators) {
3212 if (entries.length() == 0) return true;
3213 const int entries_length = entries.length(), root_index = entries_length - 1;
3214 static const int kNoDominator = -1;
3215 for (int i = 0; i < root_index; ++i) (*dominators)[i] = kNoDominator;
3216 (*dominators)[root_index] = root_index;
3217
3218 // The affected array is used to mark entries which dominators
3219 // have to be racalculated because of changes in their retainers.
3220 ScopedVector<bool> affected(entries_length);
3221 for (int i = 0; i < affected.length(); ++i) affected[i] = false;
3222 // Mark the root direct children as affected.
3223 Vector<HeapGraphEdge> children = entries[root_index]->children();
3224 for (int i = 0; i < children.length(); ++i) {
3225 affected[children[i].to()->ordered_index()] = true;
3226 }
3227
3228 bool changed = true;
3229 while (changed) {
3230 changed = false;
3231 if (!ProgressReport(true)) return false;
3232 for (int i = root_index - 1; i >= 0; --i) {
3233 if (!affected[i]) continue;
3234 affected[i] = false;
3235 // If dominator of the entry has already been set to root,
3236 // then it can't propagate any further.
3237 if ((*dominators)[i] == root_index) continue;
3238 int new_idom_index = kNoDominator;
3239 Vector<HeapGraphEdge*> rets = entries[i]->retainers();
3240 for (int j = 0; j < rets.length(); ++j) {
3241 if (rets[j]->type() == HeapGraphEdge::kShortcut) continue;
3242 int ret_index = rets[j]->From()->ordered_index();
3243 if (dominators->at(ret_index) != kNoDominator) {
3244 new_idom_index = new_idom_index == kNoDominator
3245 ? ret_index
3246 : Intersect(ret_index, new_idom_index, *dominators);
3247 // If idom has already reached the root, it doesn't make sense
3248 // to check other retainers.
3249 if (new_idom_index == root_index) break;
3250 }
3251 }
3252 if (new_idom_index != kNoDominator
3253 && dominators->at(i) != new_idom_index) {
3254 (*dominators)[i] = new_idom_index;
3255 changed = true;
3256 Vector<HeapGraphEdge> children = entries[i]->children();
3257 for (int j = 0; j < children.length(); ++j) {
3258 affected[children[j].to()->ordered_index()] = true;
3259 }
3260 }
3261 }
3262 }
3263 return true;
3264 }
3265
3266
SetEntriesDominators()3267 bool HeapSnapshotGenerator::SetEntriesDominators() {
3268 // This array is used for maintaining reverse postorder of nodes.
3269 ScopedVector<HeapEntry*> ordered_entries(snapshot_->entries()->length());
3270 FillReversePostorderIndexes(&ordered_entries);
3271 ScopedVector<int> dominators(ordered_entries.length());
3272 if (!BuildDominatorTree(ordered_entries, &dominators)) return false;
3273 for (int i = 0; i < ordered_entries.length(); ++i) {
3274 ASSERT(dominators[i] >= 0);
3275 ordered_entries[i]->set_dominator(ordered_entries[dominators[i]]);
3276 }
3277 return true;
3278 }
3279
3280
CalculateRetainedSizes()3281 bool HeapSnapshotGenerator::CalculateRetainedSizes() {
3282 // As for the dominators tree we only know parent nodes, not
3283 // children, to sum up total sizes we "bubble" node's self size
3284 // adding it to all of its parents.
3285 List<HeapEntry*>& entries = *snapshot_->entries();
3286 for (int i = 0; i < entries.length(); ++i) {
3287 HeapEntry* entry = entries[i];
3288 entry->set_retained_size(entry->self_size());
3289 }
3290 for (int i = 0; i < entries.length(); ++i) {
3291 HeapEntry* entry = entries[i];
3292 int entry_size = entry->self_size();
3293 for (HeapEntry* dominator = entry->dominator();
3294 dominator != entry;
3295 entry = dominator, dominator = entry->dominator()) {
3296 dominator->add_retained_size(entry_size);
3297 }
3298 }
3299 return true;
3300 }
3301
3302
3303 template<int bytes> struct MaxDecimalDigitsIn;
3304 template<> struct MaxDecimalDigitsIn<4> {
3305 static const int kSigned = 11;
3306 static const int kUnsigned = 10;
3307 };
3308 template<> struct MaxDecimalDigitsIn<8> {
3309 static const int kSigned = 20;
3310 static const int kUnsigned = 20;
3311 };
3312
3313
3314 class OutputStreamWriter {
3315 public:
OutputStreamWriter(v8::OutputStream * stream)3316 explicit OutputStreamWriter(v8::OutputStream* stream)
3317 : stream_(stream),
3318 chunk_size_(stream->GetChunkSize()),
3319 chunk_(chunk_size_),
3320 chunk_pos_(0),
3321 aborted_(false) {
3322 ASSERT(chunk_size_ > 0);
3323 }
aborted()3324 bool aborted() { return aborted_; }
AddCharacter(char c)3325 void AddCharacter(char c) {
3326 ASSERT(c != '\0');
3327 ASSERT(chunk_pos_ < chunk_size_);
3328 chunk_[chunk_pos_++] = c;
3329 MaybeWriteChunk();
3330 }
AddString(const char * s)3331 void AddString(const char* s) {
3332 AddSubstring(s, StrLength(s));
3333 }
AddSubstring(const char * s,int n)3334 void AddSubstring(const char* s, int n) {
3335 if (n <= 0) return;
3336 ASSERT(static_cast<size_t>(n) <= strlen(s));
3337 const char* s_end = s + n;
3338 while (s < s_end) {
3339 int s_chunk_size = Min(
3340 chunk_size_ - chunk_pos_, static_cast<int>(s_end - s));
3341 ASSERT(s_chunk_size > 0);
3342 memcpy(chunk_.start() + chunk_pos_, s, s_chunk_size);
3343 s += s_chunk_size;
3344 chunk_pos_ += s_chunk_size;
3345 MaybeWriteChunk();
3346 }
3347 }
AddNumber(int n)3348 void AddNumber(int n) { AddNumberImpl<int>(n, "%d"); }
AddNumber(unsigned n)3349 void AddNumber(unsigned n) { AddNumberImpl<unsigned>(n, "%u"); }
AddNumber(uint64_t n)3350 void AddNumber(uint64_t n) { AddNumberImpl<uint64_t>(n, "%llu"); }
Finalize()3351 void Finalize() {
3352 if (aborted_) return;
3353 ASSERT(chunk_pos_ < chunk_size_);
3354 if (chunk_pos_ != 0) {
3355 WriteChunk();
3356 }
3357 stream_->EndOfStream();
3358 }
3359
3360 private:
3361 template<typename T>
AddNumberImpl(T n,const char * format)3362 void AddNumberImpl(T n, const char* format) {
3363 // Buffer for the longest value plus trailing \0
3364 static const int kMaxNumberSize =
3365 MaxDecimalDigitsIn<sizeof(T)>::kUnsigned + 1;
3366 if (chunk_size_ - chunk_pos_ >= kMaxNumberSize) {
3367 int result = OS::SNPrintF(
3368 chunk_.SubVector(chunk_pos_, chunk_size_), format, n);
3369 ASSERT(result != -1);
3370 chunk_pos_ += result;
3371 MaybeWriteChunk();
3372 } else {
3373 EmbeddedVector<char, kMaxNumberSize> buffer;
3374 int result = OS::SNPrintF(buffer, format, n);
3375 USE(result);
3376 ASSERT(result != -1);
3377 AddString(buffer.start());
3378 }
3379 }
MaybeWriteChunk()3380 void MaybeWriteChunk() {
3381 ASSERT(chunk_pos_ <= chunk_size_);
3382 if (chunk_pos_ == chunk_size_) {
3383 WriteChunk();
3384 }
3385 }
WriteChunk()3386 void WriteChunk() {
3387 if (aborted_) return;
3388 if (stream_->WriteAsciiChunk(chunk_.start(), chunk_pos_) ==
3389 v8::OutputStream::kAbort) aborted_ = true;
3390 chunk_pos_ = 0;
3391 }
3392
3393 v8::OutputStream* stream_;
3394 int chunk_size_;
3395 ScopedVector<char> chunk_;
3396 int chunk_pos_;
3397 bool aborted_;
3398 };
3399
3400
Serialize(v8::OutputStream * stream)3401 void HeapSnapshotJSONSerializer::Serialize(v8::OutputStream* stream) {
3402 ASSERT(writer_ == NULL);
3403 writer_ = new OutputStreamWriter(stream);
3404
3405 HeapSnapshot* original_snapshot = NULL;
3406 if (snapshot_->raw_entries_size() >=
3407 SnapshotSizeConstants<kPointerSize>::kMaxSerializableSnapshotRawSize) {
3408 // The snapshot is too big. Serialize a fake snapshot.
3409 original_snapshot = snapshot_;
3410 snapshot_ = CreateFakeSnapshot();
3411 }
3412 // Since nodes graph is cyclic, we need the first pass to enumerate
3413 // them. Strings can be serialized in one pass.
3414 EnumerateNodes();
3415 SerializeImpl();
3416
3417 delete writer_;
3418 writer_ = NULL;
3419
3420 if (original_snapshot != NULL) {
3421 delete snapshot_;
3422 snapshot_ = original_snapshot;
3423 }
3424 }
3425
3426
CreateFakeSnapshot()3427 HeapSnapshot* HeapSnapshotJSONSerializer::CreateFakeSnapshot() {
3428 HeapSnapshot* result = new HeapSnapshot(snapshot_->collection(),
3429 HeapSnapshot::kFull,
3430 snapshot_->title(),
3431 snapshot_->uid());
3432 result->AllocateEntries(2, 1, 0);
3433 HeapEntry* root = result->AddRootEntry(1);
3434 const char* text = snapshot_->collection()->names()->GetFormatted(
3435 "The snapshot is too big. "
3436 "Maximum snapshot size is %" V8_PTR_PREFIX "u MB. "
3437 "Actual snapshot size is %" V8_PTR_PREFIX "u MB.",
3438 SnapshotSizeConstants<kPointerSize>::kMaxSerializableSnapshotRawSize / MB,
3439 (snapshot_->raw_entries_size() + MB - 1) / MB);
3440 HeapEntry* message = result->AddEntry(
3441 HeapEntry::kString, text, 0, 4, 0, 0);
3442 root->SetUnidirElementReference(0, 1, message);
3443 result->SetDominatorsToSelf();
3444 return result;
3445 }
3446
3447
SerializeImpl()3448 void HeapSnapshotJSONSerializer::SerializeImpl() {
3449 writer_->AddCharacter('{');
3450 writer_->AddString("\"snapshot\":{");
3451 SerializeSnapshot();
3452 if (writer_->aborted()) return;
3453 writer_->AddString("},\n");
3454 writer_->AddString("\"nodes\":[");
3455 SerializeNodes();
3456 if (writer_->aborted()) return;
3457 writer_->AddString("],\n");
3458 writer_->AddString("\"strings\":[");
3459 SerializeStrings();
3460 if (writer_->aborted()) return;
3461 writer_->AddCharacter(']');
3462 writer_->AddCharacter('}');
3463 writer_->Finalize();
3464 }
3465
3466
3467 class HeapSnapshotJSONSerializerEnumerator {
3468 public:
HeapSnapshotJSONSerializerEnumerator(HeapSnapshotJSONSerializer * s)3469 explicit HeapSnapshotJSONSerializerEnumerator(HeapSnapshotJSONSerializer* s)
3470 : s_(s) {
3471 }
Apply(HeapEntry ** entry)3472 void Apply(HeapEntry** entry) {
3473 s_->GetNodeId(*entry);
3474 }
3475 private:
3476 HeapSnapshotJSONSerializer* s_;
3477 };
3478
EnumerateNodes()3479 void HeapSnapshotJSONSerializer::EnumerateNodes() {
3480 GetNodeId(snapshot_->root()); // Make sure root gets the first id.
3481 HeapSnapshotJSONSerializerEnumerator iter(this);
3482 snapshot_->IterateEntries(&iter);
3483 }
3484
3485
GetNodeId(HeapEntry * entry)3486 int HeapSnapshotJSONSerializer::GetNodeId(HeapEntry* entry) {
3487 HashMap::Entry* cache_entry = nodes_.Lookup(entry, ObjectHash(entry), true);
3488 if (cache_entry->value == NULL) {
3489 cache_entry->value = reinterpret_cast<void*>(next_node_id_++);
3490 }
3491 return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
3492 }
3493
3494
GetStringId(const char * s)3495 int HeapSnapshotJSONSerializer::GetStringId(const char* s) {
3496 HashMap::Entry* cache_entry = strings_.Lookup(
3497 const_cast<char*>(s), ObjectHash(s), true);
3498 if (cache_entry->value == NULL) {
3499 cache_entry->value = reinterpret_cast<void*>(next_string_id_++);
3500 }
3501 return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
3502 }
3503
3504
SerializeEdge(HeapGraphEdge * edge)3505 void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge) {
3506 // The buffer needs space for 3 ints, 3 commas and \0
3507 static const int kBufferSize =
3508 MaxDecimalDigitsIn<sizeof(int)>::kSigned * 3 + 3 + 1; // NOLINT
3509 EmbeddedVector<char, kBufferSize> buffer;
3510 int edge_name_or_index = edge->type() == HeapGraphEdge::kElement
3511 || edge->type() == HeapGraphEdge::kHidden
3512 || edge->type() == HeapGraphEdge::kWeak
3513 ? edge->index() : GetStringId(edge->name());
3514 STATIC_CHECK(sizeof(int) == sizeof(edge->type())); // NOLINT
3515 STATIC_CHECK(sizeof(int) == sizeof(edge_name_or_index)); // NOLINT
3516 STATIC_CHECK(sizeof(int) == sizeof(GetNodeId(edge->to()))); // NOLINT
3517 int result = OS::SNPrintF(buffer, ",%d,%d,%d",
3518 edge->type(), edge_name_or_index, GetNodeId(edge->to()));
3519 USE(result);
3520 ASSERT(result != -1);
3521 writer_->AddString(buffer.start());
3522 }
3523
3524
SerializeNode(HeapEntry * entry)3525 void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) {
3526 // The buffer needs space for 6 ints, 1 uint32_t, 7 commas, \n and \0
3527 static const int kBufferSize =
3528 6 * MaxDecimalDigitsIn<sizeof(int)>::kSigned // NOLINT
3529 + MaxDecimalDigitsIn<sizeof(uint32_t)>::kUnsigned // NOLINT
3530 + 7 + 1 + 1;
3531 EmbeddedVector<char, kBufferSize> buffer;
3532 Vector<HeapGraphEdge> children = entry->children();
3533 STATIC_CHECK(sizeof(int) == sizeof(entry->type())); // NOLINT
3534 STATIC_CHECK(sizeof(int) == sizeof(GetStringId(entry->name()))); // NOLINT
3535 STATIC_CHECK(sizeof(unsigned) == sizeof(entry->id())); // NOLINT
3536 STATIC_CHECK(sizeof(int) == sizeof(entry->self_size())); // NOLINT
3537 STATIC_CHECK(sizeof(int) == sizeof(entry->retained_size())); // NOLINT
3538 STATIC_CHECK(sizeof(int) == sizeof(GetNodeId(entry->dominator()))); // NOLINT
3539 STATIC_CHECK(sizeof(int) == sizeof(children.length())); // NOLINT
3540 int result = OS::SNPrintF(buffer, "\n,%d,%d,%u,%d,%d,%d,%d",
3541 entry->type(),
3542 GetStringId(entry->name()),
3543 entry->id(),
3544 entry->self_size(),
3545 entry->retained_size(),
3546 GetNodeId(entry->dominator()),
3547 children.length());
3548 USE(result);
3549 ASSERT(result != -1);
3550 writer_->AddString(buffer.start());
3551 for (int i = 0; i < children.length(); ++i) {
3552 SerializeEdge(&children[i]);
3553 if (writer_->aborted()) return;
3554 }
3555 }
3556
3557
SerializeNodes()3558 void HeapSnapshotJSONSerializer::SerializeNodes() {
3559 // The first (zero) item of nodes array is an object describing node
3560 // serialization layout. We use a set of macros to improve
3561 // readability.
3562 #define JSON_A(s) "["s"]"
3563 #define JSON_O(s) "{"s"}"
3564 #define JSON_S(s) "\""s"\""
3565 writer_->AddString(JSON_O(
3566 JSON_S("fields") ":" JSON_A(
3567 JSON_S("type")
3568 "," JSON_S("name")
3569 "," JSON_S("id")
3570 "," JSON_S("self_size")
3571 "," JSON_S("retained_size")
3572 "," JSON_S("dominator")
3573 "," JSON_S("children_count")
3574 "," JSON_S("children"))
3575 "," JSON_S("types") ":" JSON_A(
3576 JSON_A(
3577 JSON_S("hidden")
3578 "," JSON_S("array")
3579 "," JSON_S("string")
3580 "," JSON_S("object")
3581 "," JSON_S("code")
3582 "," JSON_S("closure")
3583 "," JSON_S("regexp")
3584 "," JSON_S("number")
3585 "," JSON_S("native")
3586 "," JSON_S("synthetic"))
3587 "," JSON_S("string")
3588 "," JSON_S("number")
3589 "," JSON_S("number")
3590 "," JSON_S("number")
3591 "," JSON_S("number")
3592 "," JSON_S("number")
3593 "," JSON_O(
3594 JSON_S("fields") ":" JSON_A(
3595 JSON_S("type")
3596 "," JSON_S("name_or_index")
3597 "," JSON_S("to_node"))
3598 "," JSON_S("types") ":" JSON_A(
3599 JSON_A(
3600 JSON_S("context")
3601 "," JSON_S("element")
3602 "," JSON_S("property")
3603 "," JSON_S("internal")
3604 "," JSON_S("hidden")
3605 "," JSON_S("shortcut")
3606 "," JSON_S("weak"))
3607 "," JSON_S("string_or_number")
3608 "," JSON_S("node"))))));
3609 #undef JSON_S
3610 #undef JSON_O
3611 #undef JSON_A
3612
3613 const int node_fields_count = 7;
3614 // type,name,id,self_size,retained_size,dominator,children_count.
3615 const int edge_fields_count = 3; // type,name|index,to_node.
3616 List<HashMap::Entry*> sorted_nodes;
3617 SortHashMap(&nodes_, &sorted_nodes);
3618 // Rewrite node ids, so they refer to actual array positions.
3619 if (sorted_nodes.length() > 1) {
3620 // Nodes start from array index 1.
3621 int prev_value = 1;
3622 sorted_nodes[0]->value = reinterpret_cast<void*>(prev_value);
3623 for (int i = 1; i < sorted_nodes.length(); ++i) {
3624 HeapEntry* prev_heap_entry =
3625 reinterpret_cast<HeapEntry*>(sorted_nodes[i-1]->key);
3626 prev_value += node_fields_count +
3627 prev_heap_entry->children().length() * edge_fields_count;
3628 sorted_nodes[i]->value = reinterpret_cast<void*>(prev_value);
3629 }
3630 }
3631 for (int i = 0; i < sorted_nodes.length(); ++i) {
3632 SerializeNode(reinterpret_cast<HeapEntry*>(sorted_nodes[i]->key));
3633 if (writer_->aborted()) return;
3634 }
3635 }
3636
3637
SerializeSnapshot()3638 void HeapSnapshotJSONSerializer::SerializeSnapshot() {
3639 writer_->AddString("\"title\":\"");
3640 writer_->AddString(snapshot_->title());
3641 writer_->AddString("\"");
3642 writer_->AddString(",\"uid\":");
3643 writer_->AddNumber(snapshot_->uid());
3644 }
3645
3646
WriteUChar(OutputStreamWriter * w,unibrow::uchar u)3647 static void WriteUChar(OutputStreamWriter* w, unibrow::uchar u) {
3648 static const char hex_chars[] = "0123456789ABCDEF";
3649 w->AddString("\\u");
3650 w->AddCharacter(hex_chars[(u >> 12) & 0xf]);
3651 w->AddCharacter(hex_chars[(u >> 8) & 0xf]);
3652 w->AddCharacter(hex_chars[(u >> 4) & 0xf]);
3653 w->AddCharacter(hex_chars[u & 0xf]);
3654 }
3655
SerializeString(const unsigned char * s)3656 void HeapSnapshotJSONSerializer::SerializeString(const unsigned char* s) {
3657 writer_->AddCharacter('\n');
3658 writer_->AddCharacter('\"');
3659 for ( ; *s != '\0'; ++s) {
3660 switch (*s) {
3661 case '\b':
3662 writer_->AddString("\\b");
3663 continue;
3664 case '\f':
3665 writer_->AddString("\\f");
3666 continue;
3667 case '\n':
3668 writer_->AddString("\\n");
3669 continue;
3670 case '\r':
3671 writer_->AddString("\\r");
3672 continue;
3673 case '\t':
3674 writer_->AddString("\\t");
3675 continue;
3676 case '\"':
3677 case '\\':
3678 writer_->AddCharacter('\\');
3679 writer_->AddCharacter(*s);
3680 continue;
3681 default:
3682 if (*s > 31 && *s < 128) {
3683 writer_->AddCharacter(*s);
3684 } else if (*s <= 31) {
3685 // Special character with no dedicated literal.
3686 WriteUChar(writer_, *s);
3687 } else {
3688 // Convert UTF-8 into \u UTF-16 literal.
3689 unsigned length = 1, cursor = 0;
3690 for ( ; length <= 4 && *(s + length) != '\0'; ++length) { }
3691 unibrow::uchar c = unibrow::Utf8::CalculateValue(s, length, &cursor);
3692 if (c != unibrow::Utf8::kBadChar) {
3693 WriteUChar(writer_, c);
3694 ASSERT(cursor != 0);
3695 s += cursor - 1;
3696 } else {
3697 writer_->AddCharacter('?');
3698 }
3699 }
3700 }
3701 }
3702 writer_->AddCharacter('\"');
3703 }
3704
3705
SerializeStrings()3706 void HeapSnapshotJSONSerializer::SerializeStrings() {
3707 List<HashMap::Entry*> sorted_strings;
3708 SortHashMap(&strings_, &sorted_strings);
3709 writer_->AddString("\"<dummy>\"");
3710 for (int i = 0; i < sorted_strings.length(); ++i) {
3711 writer_->AddCharacter(',');
3712 SerializeString(
3713 reinterpret_cast<const unsigned char*>(sorted_strings[i]->key));
3714 if (writer_->aborted()) return;
3715 }
3716 }
3717
3718
3719 template<typename T>
SortUsingEntryValue(const T * x,const T * y)3720 inline static int SortUsingEntryValue(const T* x, const T* y) {
3721 uintptr_t x_uint = reinterpret_cast<uintptr_t>((*x)->value);
3722 uintptr_t y_uint = reinterpret_cast<uintptr_t>((*y)->value);
3723 if (x_uint > y_uint) {
3724 return 1;
3725 } else if (x_uint == y_uint) {
3726 return 0;
3727 } else {
3728 return -1;
3729 }
3730 }
3731
3732
SortHashMap(HashMap * map,List<HashMap::Entry * > * sorted_entries)3733 void HeapSnapshotJSONSerializer::SortHashMap(
3734 HashMap* map, List<HashMap::Entry*>* sorted_entries) {
3735 for (HashMap::Entry* p = map->Start(); p != NULL; p = map->Next(p))
3736 sorted_entries->Add(p);
3737 sorted_entries->Sort(SortUsingEntryValue);
3738 }
3739
3740 } } // namespace v8::internal
3741