blob: 9812c26e9e410be711b567bff01a0b8b4d061163 [file] [log] [blame]
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +00001// Copyright 2011 the V8 project authors. All rights reserved.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002// 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"
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +000029
30#include "profile-generator-inl.h"
31
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000032#include "global-handles.h"
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000033#include "heap-profiler.h"
ager@chromium.org2cc82ae2010-06-14 07:35:38 +000034#include "scopeinfo.h"
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +000035#include "unicode.h"
ager@chromium.org2cc82ae2010-06-14 07:35:38 +000036#include "zone-inl.h"
fschneider@chromium.org086aac62010-03-17 13:18:24 +000037
fschneider@chromium.org086aac62010-03-17 13:18:24 +000038namespace v8 {
39namespace internal {
40
41
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000042TokenEnumerator::TokenEnumerator()
43 : token_locations_(4),
44 token_removed_(4) {
45}
46
47
48TokenEnumerator::~TokenEnumerator() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000049 Isolate* isolate = Isolate::Current();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000050 for (int i = 0; i < token_locations_.length(); ++i) {
51 if (!token_removed_[i]) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000052 isolate->global_handles()->ClearWeakness(token_locations_[i]);
53 isolate->global_handles()->Destroy(token_locations_[i]);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000054 }
55 }
56}
57
58
59int TokenEnumerator::GetTokenId(Object* token) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000060 Isolate* isolate = Isolate::Current();
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +000061 if (token == NULL) return TokenEnumerator::kNoSecurityToken;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000062 for (int i = 0; i < token_locations_.length(); ++i) {
63 if (*token_locations_[i] == token && !token_removed_[i]) return i;
64 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000065 Handle<Object> handle = isolate->global_handles()->Create(token);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000066 // handle.location() points to a memory cell holding a pointer
67 // to a token object in the V8's heap.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000068 isolate->global_handles()->MakeWeak(handle.location(), this,
69 TokenRemovedCallback);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000070 token_locations_.Add(handle.location());
71 token_removed_.Add(false);
72 return token_locations_.length() - 1;
73}
74
75
76void TokenEnumerator::TokenRemovedCallback(v8::Persistent<v8::Value> handle,
77 void* parameter) {
78 reinterpret_cast<TokenEnumerator*>(parameter)->TokenRemoved(
79 Utils::OpenHandle(*handle).location());
fschneider@chromium.orged78ffd2010-07-21 11:05:19 +000080 handle.Dispose();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000081}
82
83
84void 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
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +000094StringsStorage::StringsStorage()
95 : names_(StringsMatch) {
96}
97
98
99StringsStorage::~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 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000105}
106
107
108const 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 = HashSequentialString(dst.start(), len);
114 return AddOrDisposeString(dst.start(), hash);
115}
116
117
118const char* StringsStorage::GetFormatted(const char* format, ...) {
119 va_list args;
120 va_start(args, format);
121 const char* result = GetVFormatted(format, args);
122 va_end(args);
123 return result;
124}
125
126
127const char* StringsStorage::AddOrDisposeString(char* str, uint32_t hash) {
128 HashMap::Entry* cache_entry = names_.Lookup(str, hash, true);
129 if (cache_entry->value == NULL) {
130 // New entry added.
131 cache_entry->value = str;
132 } else {
133 DeleteArray(str);
134 }
135 return reinterpret_cast<const char*>(cache_entry->value);
136}
137
138
139const char* StringsStorage::GetVFormatted(const char* format, va_list args) {
140 Vector<char> str = Vector<char>::New(1024);
141 int len = OS::VSNPrintF(str, format, args);
142 if (len == -1) {
143 DeleteArray(str.start());
144 return format;
145 }
146 uint32_t hash = HashSequentialString(str.start(), len);
147 return AddOrDisposeString(str.start(), hash);
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000148}
149
150
151const char* StringsStorage::GetName(String* name) {
152 if (name->IsString()) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000153 return AddOrDisposeString(
154 name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).Detach(),
155 name->Hash());
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000156 }
157 return "";
158}
159
160
vegorov@chromium.org42841962010-10-18 11:18:59 +0000161const char* StringsStorage::GetName(int index) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000162 return GetFormatted("%d", index);
vegorov@chromium.org42841962010-10-18 11:18:59 +0000163}
164
165
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000166const char* const CodeEntry::kEmptyNamePrefix = "";
lrn@chromium.org25156de2010-04-06 13:10:27 +0000167
168
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000169void CodeEntry::CopyData(const CodeEntry& source) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000170 tag_ = source.tag_;
171 name_prefix_ = source.name_prefix_;
172 name_ = source.name_;
173 resource_name_ = source.resource_name_;
174 line_number_ = source.line_number_;
175}
176
177
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000178uint32_t CodeEntry::GetCallUid() const {
179 uint32_t hash = ComputeIntegerHash(tag_);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000180 if (shared_id_ != 0) {
181 hash ^= ComputeIntegerHash(
182 static_cast<uint32_t>(shared_id_));
183 } else {
184 hash ^= ComputeIntegerHash(
185 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_prefix_)));
186 hash ^= ComputeIntegerHash(
187 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_)));
188 hash ^= ComputeIntegerHash(
189 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(resource_name_)));
190 hash ^= ComputeIntegerHash(line_number_);
191 }
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000192 return hash;
193}
194
195
196bool CodeEntry::IsSameAs(CodeEntry* entry) const {
197 return this == entry
198 || (tag_ == entry->tag_
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000199 && shared_id_ == entry->shared_id_
200 && (shared_id_ != 0
201 || (name_prefix_ == entry->name_prefix_
202 && name_ == entry->name_
203 && resource_name_ == entry->resource_name_
204 && line_number_ == entry->line_number_)));
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000205}
206
207
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000208ProfileNode* ProfileNode::FindChild(CodeEntry* entry) {
209 HashMap::Entry* map_entry =
210 children_.Lookup(entry, CodeEntryHash(entry), false);
211 return map_entry != NULL ?
212 reinterpret_cast<ProfileNode*>(map_entry->value) : NULL;
213}
214
215
216ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) {
217 HashMap::Entry* map_entry =
218 children_.Lookup(entry, CodeEntryHash(entry), true);
219 if (map_entry->value == NULL) {
220 // New node added.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000221 ProfileNode* new_node = new ProfileNode(tree_, entry);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000222 map_entry->value = new_node;
223 children_list_.Add(new_node);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000224 }
225 return reinterpret_cast<ProfileNode*>(map_entry->value);
226}
227
228
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000229double ProfileNode::GetSelfMillis() const {
230 return tree_->TicksToMillis(self_ticks_);
231}
232
233
234double ProfileNode::GetTotalMillis() const {
235 return tree_->TicksToMillis(total_ticks_);
236}
237
238
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000239void ProfileNode::Print(int indent) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000240 OS::Print("%5u %5u %*c %s%s [%d]",
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000241 total_ticks_, self_ticks_,
242 indent, ' ',
ager@chromium.org357bf652010-04-12 11:30:10 +0000243 entry_->name_prefix(),
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000244 entry_->name(),
245 entry_->security_token_id());
ager@chromium.org357bf652010-04-12 11:30:10 +0000246 if (entry_->resource_name()[0] != '\0')
247 OS::Print(" %s:%d", entry_->resource_name(), entry_->line_number());
248 OS::Print("\n");
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000249 for (HashMap::Entry* p = children_.Start();
250 p != NULL;
251 p = children_.Next(p)) {
252 reinterpret_cast<ProfileNode*>(p->value)->Print(indent + 2);
253 }
254}
255
256
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000257class DeleteNodesCallback {
258 public:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000259 void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }
260
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000261 void AfterAllChildrenTraversed(ProfileNode* node) {
262 delete node;
263 }
264
265 void AfterChildTraversed(ProfileNode*, ProfileNode*) { }
266};
267
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000268
ager@chromium.org357bf652010-04-12 11:30:10 +0000269ProfileTree::ProfileTree()
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000270 : root_entry_(Logger::FUNCTION_TAG,
271 "",
272 "(root)",
273 "",
274 0,
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000275 TokenEnumerator::kNoSecurityToken),
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000276 root_(new ProfileNode(this, &root_entry_)) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000277}
278
279
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000280ProfileTree::~ProfileTree() {
281 DeleteNodesCallback cb;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000282 TraverseDepthFirst(&cb);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000283}
284
285
286void ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path) {
287 ProfileNode* node = root_;
288 for (CodeEntry** entry = path.start() + path.length() - 1;
289 entry != path.start() - 1;
290 --entry) {
291 if (*entry != NULL) {
292 node = node->FindOrAddChild(*entry);
293 }
294 }
295 node->IncrementSelfTicks();
296}
297
298
299void ProfileTree::AddPathFromStart(const Vector<CodeEntry*>& path) {
300 ProfileNode* node = root_;
301 for (CodeEntry** entry = path.start();
302 entry != path.start() + path.length();
303 ++entry) {
304 if (*entry != NULL) {
305 node = node->FindOrAddChild(*entry);
306 }
307 }
308 node->IncrementSelfTicks();
309}
310
311
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000312struct NodesPair {
313 NodesPair(ProfileNode* src, ProfileNode* dst)
314 : src(src), dst(dst) { }
315 ProfileNode* src;
316 ProfileNode* dst;
317};
318
319
320class FilteredCloneCallback {
321 public:
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000322 FilteredCloneCallback(ProfileNode* dst_root, int security_token_id)
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000323 : stack_(10),
324 security_token_id_(security_token_id) {
325 stack_.Add(NodesPair(NULL, dst_root));
326 }
327
328 void BeforeTraversingChild(ProfileNode* parent, ProfileNode* child) {
329 if (IsTokenAcceptable(child->entry()->security_token_id(),
330 parent->entry()->security_token_id())) {
331 ProfileNode* clone = stack_.last().dst->FindOrAddChild(child->entry());
332 clone->IncreaseSelfTicks(child->self_ticks());
333 stack_.Add(NodesPair(child, clone));
334 } else {
335 // Attribute ticks to parent node.
336 stack_.last().dst->IncreaseSelfTicks(child->self_ticks());
337 }
338 }
339
340 void AfterAllChildrenTraversed(ProfileNode* parent) { }
341
342 void AfterChildTraversed(ProfileNode*, ProfileNode* child) {
343 if (stack_.last().src == child) {
344 stack_.RemoveLast();
345 }
346 }
347
348 private:
349 bool IsTokenAcceptable(int token, int parent_token) {
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000350 if (token == TokenEnumerator::kNoSecurityToken
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000351 || token == security_token_id_) return true;
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000352 if (token == TokenEnumerator::kInheritsSecurityToken) {
353 ASSERT(parent_token != TokenEnumerator::kInheritsSecurityToken);
354 return parent_token == TokenEnumerator::kNoSecurityToken
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000355 || parent_token == security_token_id_;
356 }
357 return false;
358 }
359
360 List<NodesPair> stack_;
361 int security_token_id_;
362};
363
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000364void ProfileTree::FilteredClone(ProfileTree* src, int security_token_id) {
365 ms_to_ticks_scale_ = src->ms_to_ticks_scale_;
366 FilteredCloneCallback cb(root_, security_token_id);
367 src->TraverseDepthFirst(&cb);
368 CalculateTotalTicks();
369}
370
371
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000372void ProfileTree::SetTickRatePerMs(double ticks_per_ms) {
373 ms_to_ticks_scale_ = ticks_per_ms > 0 ? 1.0 / ticks_per_ms : 1.0;
374}
375
376
lrn@chromium.org25156de2010-04-06 13:10:27 +0000377class Position {
378 public:
379 explicit Position(ProfileNode* node)
380 : node(node), child_idx_(0) { }
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000381 INLINE(ProfileNode* current_child()) {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000382 return node->children()->at(child_idx_);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000383 }
lrn@chromium.org25156de2010-04-06 13:10:27 +0000384 INLINE(bool has_current_child()) {
385 return child_idx_ < node->children()->length();
386 }
387 INLINE(void next_child()) { ++child_idx_; }
388
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000389 ProfileNode* node;
lrn@chromium.org25156de2010-04-06 13:10:27 +0000390 private:
391 int child_idx_;
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000392};
393
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000394
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000395// Non-recursive implementation of a depth-first post-order tree traversal.
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000396template <typename Callback>
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000397void ProfileTree::TraverseDepthFirst(Callback* callback) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000398 List<Position> stack(10);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000399 stack.Add(Position(root_));
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000400 while (stack.length() > 0) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000401 Position& current = stack.last();
lrn@chromium.org25156de2010-04-06 13:10:27 +0000402 if (current.has_current_child()) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000403 callback->BeforeTraversingChild(current.node, current.current_child());
lrn@chromium.org25156de2010-04-06 13:10:27 +0000404 stack.Add(Position(current.current_child()));
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000405 } else {
406 callback->AfterAllChildrenTraversed(current.node);
407 if (stack.length() > 1) {
408 Position& parent = stack[stack.length() - 2];
409 callback->AfterChildTraversed(parent.node, current.node);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000410 parent.next_child();
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000411 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000412 // Remove child from the stack.
413 stack.RemoveLast();
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000414 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000415 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000416}
417
418
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000419class CalculateTotalTicksCallback {
420 public:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000421 void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }
422
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000423 void AfterAllChildrenTraversed(ProfileNode* node) {
424 node->IncreaseTotalTicks(node->self_ticks());
425 }
426
427 void AfterChildTraversed(ProfileNode* parent, ProfileNode* child) {
428 parent->IncreaseTotalTicks(child->total_ticks());
429 }
430};
431
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000432
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000433void ProfileTree::CalculateTotalTicks() {
434 CalculateTotalTicksCallback cb;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000435 TraverseDepthFirst(&cb);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000436}
437
438
439void ProfileTree::ShortPrint() {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000440 OS::Print("root: %u %u %.2fms %.2fms\n",
441 root_->total_ticks(), root_->self_ticks(),
442 root_->GetTotalMillis(), root_->GetSelfMillis());
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000443}
444
445
446void CpuProfile::AddPath(const Vector<CodeEntry*>& path) {
447 top_down_.AddPathFromEnd(path);
448 bottom_up_.AddPathFromStart(path);
449}
450
451
452void CpuProfile::CalculateTotalTicks() {
453 top_down_.CalculateTotalTicks();
454 bottom_up_.CalculateTotalTicks();
455}
456
457
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000458void CpuProfile::SetActualSamplingRate(double actual_sampling_rate) {
459 top_down_.SetTickRatePerMs(actual_sampling_rate);
460 bottom_up_.SetTickRatePerMs(actual_sampling_rate);
461}
462
463
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000464CpuProfile* CpuProfile::FilteredClone(int security_token_id) {
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000465 ASSERT(security_token_id != TokenEnumerator::kNoSecurityToken);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000466 CpuProfile* clone = new CpuProfile(title_, uid_);
467 clone->top_down_.FilteredClone(&top_down_, security_token_id);
468 clone->bottom_up_.FilteredClone(&bottom_up_, security_token_id);
469 return clone;
470}
471
472
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000473void CpuProfile::ShortPrint() {
474 OS::Print("top down ");
475 top_down_.ShortPrint();
476 OS::Print("bottom up ");
477 bottom_up_.ShortPrint();
478}
479
480
481void CpuProfile::Print() {
482 OS::Print("[Top down]:\n");
483 top_down_.Print();
484 OS::Print("[Bottom up]:\n");
485 bottom_up_.Print();
486}
487
488
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000489CodeEntry* const CodeMap::kSharedFunctionCodeEntry = NULL;
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000490const CodeMap::CodeTreeConfig::Key CodeMap::CodeTreeConfig::kNoKey = NULL;
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000491
492
lrn@chromium.org34e60782011-09-15 07:25:40 +0000493void CodeMap::AddCode(Address addr, CodeEntry* entry, unsigned size) {
494 DeleteAllCoveredCode(addr, addr + size);
495 CodeTree::Locator locator;
496 tree_.Insert(addr, &locator);
497 locator.set_value(CodeEntryInfo(entry, size));
498}
499
500
501void CodeMap::DeleteAllCoveredCode(Address start, Address end) {
502 List<Address> to_delete;
503 Address addr = end - 1;
504 while (addr >= start) {
505 CodeTree::Locator locator;
506 if (!tree_.FindGreatestLessThan(addr, &locator)) break;
507 Address start2 = locator.key(), end2 = start2 + locator.value().size;
508 if (start2 < end && start < end2) to_delete.Add(start2);
509 addr = start2 - 1;
510 }
511 for (int i = 0; i < to_delete.length(); ++i) tree_.Remove(to_delete[i]);
512}
513
514
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000515CodeEntry* CodeMap::FindEntry(Address addr) {
516 CodeTree::Locator locator;
517 if (tree_.FindGreatestLessThan(addr, &locator)) {
518 // locator.key() <= addr. Need to check that addr is within entry.
519 const CodeEntryInfo& entry = locator.value();
520 if (addr < (locator.key() + entry.size))
521 return entry.entry;
522 }
523 return NULL;
524}
525
526
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000527int CodeMap::GetSharedId(Address addr) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000528 CodeTree::Locator locator;
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000529 // For shared function entries, 'size' field is used to store their IDs.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000530 if (tree_.Find(addr, &locator)) {
531 const CodeEntryInfo& entry = locator.value();
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000532 ASSERT(entry.entry == kSharedFunctionCodeEntry);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000533 return entry.size;
534 } else {
535 tree_.Insert(addr, &locator);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000536 int id = next_shared_id_++;
537 locator.set_value(CodeEntryInfo(kSharedFunctionCodeEntry, id));
538 return id;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000539 }
540}
541
542
lrn@chromium.org34e60782011-09-15 07:25:40 +0000543void CodeMap::MoveCode(Address from, Address to) {
544 if (from == to) return;
545 CodeTree::Locator locator;
546 if (!tree_.Find(from, &locator)) return;
547 CodeEntryInfo entry = locator.value();
548 tree_.Remove(from);
549 AddCode(to, entry.entry, entry.size);
550}
551
552
lrn@chromium.org25156de2010-04-06 13:10:27 +0000553void CodeMap::CodeTreePrinter::Call(
554 const Address& key, const CodeMap::CodeEntryInfo& value) {
555 OS::Print("%p %5d %s\n", key, value.size, value.entry->name());
556}
557
558
559void CodeMap::Print() {
560 CodeTreePrinter printer;
561 tree_.ForEach(&printer);
562}
563
564
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000565CpuProfilesCollection::CpuProfilesCollection()
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000566 : profiles_uids_(UidsMatch),
lrn@chromium.org25156de2010-04-06 13:10:27 +0000567 current_profiles_semaphore_(OS::CreateSemaphore(1)) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000568 // Create list of unabridged profiles.
569 profiles_by_token_.Add(new List<CpuProfile*>());
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000570}
571
572
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000573static void DeleteCodeEntry(CodeEntry** entry_ptr) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000574 delete *entry_ptr;
575}
576
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000577static void DeleteCpuProfile(CpuProfile** profile_ptr) {
578 delete *profile_ptr;
579}
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000580
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000581static void DeleteProfilesList(List<CpuProfile*>** list_ptr) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000582 if (*list_ptr != NULL) {
583 (*list_ptr)->Iterate(DeleteCpuProfile);
584 delete *list_ptr;
585 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000586}
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000587
588CpuProfilesCollection::~CpuProfilesCollection() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000589 delete current_profiles_semaphore_;
590 current_profiles_.Iterate(DeleteCpuProfile);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000591 detached_profiles_.Iterate(DeleteCpuProfile);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000592 profiles_by_token_.Iterate(DeleteProfilesList);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000593 code_entries_.Iterate(DeleteCodeEntry);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000594}
595
596
lrn@chromium.org25156de2010-04-06 13:10:27 +0000597bool CpuProfilesCollection::StartProfiling(const char* title, unsigned uid) {
598 ASSERT(uid > 0);
599 current_profiles_semaphore_->Wait();
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +0000600 if (current_profiles_.length() >= kMaxSimultaneousProfiles) {
601 current_profiles_semaphore_->Signal();
602 return false;
603 }
lrn@chromium.org25156de2010-04-06 13:10:27 +0000604 for (int i = 0; i < current_profiles_.length(); ++i) {
605 if (strcmp(current_profiles_[i]->title(), title) == 0) {
606 // Ignore attempts to start profile with the same title.
607 current_profiles_semaphore_->Signal();
608 return false;
609 }
610 }
611 current_profiles_.Add(new CpuProfile(title, uid));
612 current_profiles_semaphore_->Signal();
613 return true;
614}
615
616
617bool CpuProfilesCollection::StartProfiling(String* title, unsigned uid) {
618 return StartProfiling(GetName(title), uid);
619}
620
621
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000622CpuProfile* CpuProfilesCollection::StopProfiling(int security_token_id,
623 const char* title,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000624 double actual_sampling_rate) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000625 const int title_len = StrLength(title);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000626 CpuProfile* profile = NULL;
627 current_profiles_semaphore_->Wait();
628 for (int i = current_profiles_.length() - 1; i >= 0; --i) {
629 if (title_len == 0 || strcmp(current_profiles_[i]->title(), title) == 0) {
630 profile = current_profiles_.Remove(i);
631 break;
632 }
633 }
634 current_profiles_semaphore_->Signal();
635
636 if (profile != NULL) {
637 profile->CalculateTotalTicks();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000638 profile->SetActualSamplingRate(actual_sampling_rate);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000639 List<CpuProfile*>* unabridged_list =
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000640 profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000641 unabridged_list->Add(profile);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000642 HashMap::Entry* entry =
643 profiles_uids_.Lookup(reinterpret_cast<void*>(profile->uid()),
644 static_cast<uint32_t>(profile->uid()),
645 true);
646 ASSERT(entry->value == NULL);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000647 entry->value = reinterpret_cast<void*>(unabridged_list->length() - 1);
648 return GetProfile(security_token_id, profile->uid());
lrn@chromium.org25156de2010-04-06 13:10:27 +0000649 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000650 return NULL;
lrn@chromium.org25156de2010-04-06 13:10:27 +0000651}
652
653
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000654CpuProfile* CpuProfilesCollection::GetProfile(int security_token_id,
655 unsigned uid) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000656 int index = GetProfileIndex(uid);
657 if (index < 0) return NULL;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000658 List<CpuProfile*>* unabridged_list =
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000659 profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
660 if (security_token_id == TokenEnumerator::kNoSecurityToken) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000661 return unabridged_list->at(index);
662 }
663 List<CpuProfile*>* list = GetProfilesList(security_token_id);
664 if (list->at(index) == NULL) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000665 (*list)[index] =
666 unabridged_list->at(index)->FilteredClone(security_token_id);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000667 }
668 return list->at(index);
669}
670
671
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000672int CpuProfilesCollection::GetProfileIndex(unsigned uid) {
673 HashMap::Entry* entry = profiles_uids_.Lookup(reinterpret_cast<void*>(uid),
674 static_cast<uint32_t>(uid),
675 false);
676 return entry != NULL ?
677 static_cast<int>(reinterpret_cast<intptr_t>(entry->value)) : -1;
678}
679
680
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000681bool CpuProfilesCollection::IsLastProfile(const char* title) {
682 // Called from VM thread, and only it can mutate the list,
683 // so no locking is needed here.
684 if (current_profiles_.length() != 1) return false;
685 return StrLength(title) == 0
686 || strcmp(current_profiles_[0]->title(), title) == 0;
687}
688
689
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000690void CpuProfilesCollection::RemoveProfile(CpuProfile* profile) {
691 // Called from VM thread for a completed profile.
692 unsigned uid = profile->uid();
693 int index = GetProfileIndex(uid);
694 if (index < 0) {
695 detached_profiles_.RemoveElement(profile);
696 return;
697 }
698 profiles_uids_.Remove(reinterpret_cast<void*>(uid),
699 static_cast<uint32_t>(uid));
700 // Decrement all indexes above the deleted one.
701 for (HashMap::Entry* p = profiles_uids_.Start();
702 p != NULL;
703 p = profiles_uids_.Next(p)) {
704 intptr_t p_index = reinterpret_cast<intptr_t>(p->value);
705 if (p_index > index) {
706 p->value = reinterpret_cast<void*>(p_index - 1);
707 }
708 }
709 for (int i = 0; i < profiles_by_token_.length(); ++i) {
710 List<CpuProfile*>* list = profiles_by_token_[i];
711 if (list != NULL && index < list->length()) {
712 // Move all filtered clones into detached_profiles_,
713 // so we can know that they are still in use.
714 CpuProfile* cloned_profile = list->Remove(index);
715 if (cloned_profile != NULL && cloned_profile != profile) {
716 detached_profiles_.Add(cloned_profile);
717 }
718 }
719 }
720}
721
722
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000723int CpuProfilesCollection::TokenToIndex(int security_token_id) {
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000724 ASSERT(TokenEnumerator::kNoSecurityToken == -1);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000725 return security_token_id + 1; // kNoSecurityToken -> 0, 0 -> 1, ...
726}
727
728
729List<CpuProfile*>* CpuProfilesCollection::GetProfilesList(
730 int security_token_id) {
731 const int index = TokenToIndex(security_token_id);
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000732 const int lists_to_add = index - profiles_by_token_.length() + 1;
733 if (lists_to_add > 0) profiles_by_token_.AddBlock(NULL, lists_to_add);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000734 List<CpuProfile*>* unabridged_list =
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000735 profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000736 const int current_count = unabridged_list->length();
737 if (profiles_by_token_[index] == NULL) {
738 profiles_by_token_[index] = new List<CpuProfile*>(current_count);
739 }
740 List<CpuProfile*>* list = profiles_by_token_[index];
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000741 const int profiles_to_add = current_count - list->length();
742 if (profiles_to_add > 0) list->AddBlock(NULL, profiles_to_add);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000743 return list;
744}
745
746
747List<CpuProfile*>* CpuProfilesCollection::Profiles(int security_token_id) {
748 List<CpuProfile*>* unabridged_list =
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000749 profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
750 if (security_token_id == TokenEnumerator::kNoSecurityToken) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000751 return unabridged_list;
752 }
753 List<CpuProfile*>* list = GetProfilesList(security_token_id);
754 const int current_count = unabridged_list->length();
755 for (int i = 0; i < current_count; ++i) {
756 if (list->at(i) == NULL) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000757 (*list)[i] = unabridged_list->at(i)->FilteredClone(security_token_id);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000758 }
759 }
760 return list;
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000761}
762
763
764CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
765 String* name,
766 String* resource_name,
767 int line_number) {
768 CodeEntry* entry = new CodeEntry(tag,
lrn@chromium.org25156de2010-04-06 13:10:27 +0000769 CodeEntry::kEmptyNamePrefix,
ager@chromium.org357bf652010-04-12 11:30:10 +0000770 GetFunctionName(name),
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000771 GetName(resource_name),
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000772 line_number,
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000773 TokenEnumerator::kNoSecurityToken);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000774 code_entries_.Add(entry);
775 return entry;
776}
777
778
779CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
780 const char* name) {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000781 CodeEntry* entry = new CodeEntry(tag,
782 CodeEntry::kEmptyNamePrefix,
ager@chromium.org357bf652010-04-12 11:30:10 +0000783 GetFunctionName(name),
lrn@chromium.org25156de2010-04-06 13:10:27 +0000784 "",
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000785 v8::CpuProfileNode::kNoLineNumberInfo,
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000786 TokenEnumerator::kNoSecurityToken);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000787 code_entries_.Add(entry);
788 return entry;
789}
790
791
792CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
793 const char* name_prefix,
794 String* name) {
795 CodeEntry* entry = new CodeEntry(tag,
796 name_prefix,
797 GetName(name),
798 "",
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000799 v8::CpuProfileNode::kNoLineNumberInfo,
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000800 TokenEnumerator::kInheritsSecurityToken);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000801 code_entries_.Add(entry);
802 return entry;
803}
804
805
806CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
807 int args_count) {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000808 CodeEntry* entry = new CodeEntry(tag,
809 "args_count: ",
810 GetName(args_count),
811 "",
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000812 v8::CpuProfileNode::kNoLineNumberInfo,
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000813 TokenEnumerator::kInheritsSecurityToken);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000814 code_entries_.Add(entry);
815 return entry;
816}
817
818
lrn@chromium.org25156de2010-04-06 13:10:27 +0000819void CpuProfilesCollection::AddPathToCurrentProfiles(
820 const Vector<CodeEntry*>& path) {
821 // As starting / stopping profiles is rare relatively to this
822 // method, we don't bother minimizing the duration of lock holding,
823 // e.g. copying contents of the list to a local vector.
824 current_profiles_semaphore_->Wait();
825 for (int i = 0; i < current_profiles_.length(); ++i) {
826 current_profiles_[i]->AddPath(path);
827 }
828 current_profiles_semaphore_->Signal();
829}
830
831
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000832void SampleRateCalculator::Tick() {
833 if (--wall_time_query_countdown_ == 0)
834 UpdateMeasurements(OS::TimeCurrentMillis());
835}
836
837
838void SampleRateCalculator::UpdateMeasurements(double current_time) {
839 if (measurements_count_++ != 0) {
840 const double measured_ticks_per_ms =
841 (kWallTimeQueryIntervalMs * ticks_per_ms_) /
842 (current_time - last_wall_time_);
843 // Update the average value.
844 ticks_per_ms_ +=
845 (measured_ticks_per_ms - ticks_per_ms_) / measurements_count_;
846 // Update the externally accessible result.
847 result_ = static_cast<AtomicWord>(ticks_per_ms_ * kResultScale);
848 }
849 last_wall_time_ = current_time;
850 wall_time_query_countdown_ =
851 static_cast<unsigned>(kWallTimeQueryIntervalMs * ticks_per_ms_);
852}
853
854
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000855const char* const ProfileGenerator::kAnonymousFunctionName =
856 "(anonymous function)";
857const char* const ProfileGenerator::kProgramEntryName =
858 "(program)";
859const char* const ProfileGenerator::kGarbageCollectorEntryName =
860 "(garbage collector)";
ager@chromium.org357bf652010-04-12 11:30:10 +0000861
862
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000863ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles)
ager@chromium.org357bf652010-04-12 11:30:10 +0000864 : profiles_(profiles),
865 program_entry_(
866 profiles->NewCodeEntry(Logger::FUNCTION_TAG, kProgramEntryName)),
867 gc_entry_(
868 profiles->NewCodeEntry(Logger::BUILTIN_TAG,
869 kGarbageCollectorEntryName)) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000870}
871
872
873void ProfileGenerator::RecordTickSample(const TickSample& sample) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000874 // Allocate space for stack frames + pc + function + vm-state.
875 ScopedVector<CodeEntry*> entries(sample.frames_count + 3);
876 // As actual number of decoded code entries may vary, initialize
877 // entries vector with NULL values.
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000878 CodeEntry** entry = entries.start();
ager@chromium.org357bf652010-04-12 11:30:10 +0000879 memset(entry, 0, entries.length() * sizeof(*entry));
880 if (sample.pc != NULL) {
881 *entry++ = code_map_.FindEntry(sample.pc);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000882
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000883 if (sample.has_external_callback) {
884 // Don't use PC when in external callback code, as it can point
885 // inside callback's code, and we will erroneously report
886 // that a callback calls itself.
887 *(entries.start()) = NULL;
888 *entry++ = code_map_.FindEntry(sample.external_callback);
889 } else if (sample.tos != NULL) {
890 // Find out, if top of stack was pointing inside a JS function
891 // meaning that we have encountered a frameless invocation.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000892 *entry = code_map_.FindEntry(sample.tos);
ager@chromium.org357bf652010-04-12 11:30:10 +0000893 if (*entry != NULL && !(*entry)->is_js_function()) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000894 *entry = NULL;
ager@chromium.org357bf652010-04-12 11:30:10 +0000895 }
896 entry++;
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000897 }
ager@chromium.org357bf652010-04-12 11:30:10 +0000898
899 for (const Address *stack_pos = sample.stack,
900 *stack_end = stack_pos + sample.frames_count;
901 stack_pos != stack_end;
902 ++stack_pos) {
903 *entry++ = code_map_.FindEntry(*stack_pos);
904 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000905 }
906
ager@chromium.org357bf652010-04-12 11:30:10 +0000907 if (FLAG_prof_browser_mode) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000908 bool no_symbolized_entries = true;
909 for (CodeEntry** e = entries.start(); e != entry; ++e) {
910 if (*e != NULL) {
911 no_symbolized_entries = false;
912 break;
913 }
914 }
915 // If no frames were symbolized, put the VM state entry in.
916 if (no_symbolized_entries) {
917 *entry++ = EntryForVMState(sample.state);
918 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000919 }
920
lrn@chromium.org25156de2010-04-06 13:10:27 +0000921 profiles_->AddPathToCurrentProfiles(entries);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000922}
923
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000924
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000925void HeapGraphEdge::Init(
926 int child_index, Type type, const char* name, HeapEntry* to) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +0000927 ASSERT(type == kContextVariable
928 || type == kProperty
929 || type == kInternal
930 || type == kShortcut);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000931 child_index_ = child_index;
932 type_ = type;
933 name_ = name;
934 to_ = to;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000935}
936
937
vegorov@chromium.org21b5e952010-11-23 10:24:40 +0000938void HeapGraphEdge::Init(int child_index, Type type, int index, HeapEntry* to) {
939 ASSERT(type == kElement || type == kHidden);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000940 child_index_ = child_index;
vegorov@chromium.org21b5e952010-11-23 10:24:40 +0000941 type_ = type;
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000942 index_ = index;
943 to_ = to;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000944}
945
946
vegorov@chromium.org21b5e952010-11-23 10:24:40 +0000947void HeapGraphEdge::Init(int child_index, int index, HeapEntry* to) {
948 Init(child_index, kElement, index, to);
949}
950
951
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000952HeapEntry* HeapGraphEdge::From() {
953 return reinterpret_cast<HeapEntry*>(this - child_index_) - 1;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000954}
955
956
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000957void HeapEntry::Init(HeapSnapshot* snapshot,
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000958 Type type,
959 const char* name,
960 uint64_t id,
961 int self_size,
962 int children_count,
963 int retainers_count) {
964 snapshot_ = snapshot;
965 type_ = type;
966 painted_ = kUnpainted;
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000967 name_ = name;
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000968 self_size_ = self_size;
vegorov@chromium.org21b5e952010-11-23 10:24:40 +0000969 retained_size_ = 0;
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000970 children_count_ = children_count;
971 retainers_count_ = retainers_count;
vegorov@chromium.org21b5e952010-11-23 10:24:40 +0000972 dominator_ = NULL;
973
974 union {
975 uint64_t set_id;
976 Id stored_id;
977 } id_adaptor = {id};
978 id_ = id_adaptor.stored_id;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000979}
980
981
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000982void HeapEntry::SetNamedReference(HeapGraphEdge::Type type,
983 int child_index,
984 const char* name,
985 HeapEntry* entry,
986 int retainer_index) {
987 children_arr()[child_index].Init(child_index, type, name, entry);
988 entry->retainers_arr()[retainer_index] = children_arr() + child_index;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000989}
990
991
vegorov@chromium.org21b5e952010-11-23 10:24:40 +0000992void HeapEntry::SetIndexedReference(HeapGraphEdge::Type type,
993 int child_index,
994 int index,
995 HeapEntry* entry,
996 int retainer_index) {
997 children_arr()[child_index].Init(child_index, type, index, entry);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000998 entry->retainers_arr()[retainer_index] = children_arr() + child_index;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000999}
1000
1001
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001002void HeapEntry::SetUnidirElementReference(
1003 int child_index, int index, HeapEntry* entry) {
1004 children_arr()[child_index].Init(child_index, index, entry);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00001005}
1006
1007
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001008int HeapEntry::RetainedSize(bool exact) {
1009 if (exact && (retained_size_ & kExactRetainedSizeTag) == 0) {
1010 CalculateExactRetainedSize();
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001011 }
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001012 return retained_size_ & (~kExactRetainedSizeTag);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001013}
1014
1015
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001016Handle<HeapObject> HeapEntry::GetHeapObject() {
1017 return snapshot_->collection()->FindHeapObjectById(id());
1018}
1019
1020
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001021template<class Visitor>
1022void HeapEntry::ApplyAndPaintAllReachable(Visitor* visitor) {
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001023 List<HeapEntry*> list(10);
1024 list.Add(this);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001025 this->paint_reachable();
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001026 visitor->Apply(this);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001027 while (!list.is_empty()) {
1028 HeapEntry* entry = list.RemoveLast();
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001029 Vector<HeapGraphEdge> children = entry->children();
1030 for (int i = 0; i < children.length(); ++i) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001031 if (children[i].type() == HeapGraphEdge::kShortcut) continue;
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001032 HeapEntry* child = children[i].to();
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001033 if (!child->painted_reachable()) {
1034 list.Add(child);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001035 child->paint_reachable();
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001036 visitor->Apply(child);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001037 }
1038 }
1039 }
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001040}
1041
1042
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001043class NullClass {
1044 public:
1045 void Apply(HeapEntry* entry) { }
1046};
1047
1048void HeapEntry::PaintAllReachable() {
1049 NullClass null;
1050 ApplyAndPaintAllReachable(&null);
1051}
1052
1053
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001054void HeapEntry::Print(int max_depth, int indent) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001055 OS::Print("%6d %6d [%llu] ", self_size(), RetainedSize(false), id());
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001056 if (type() != kString) {
1057 OS::Print("%s %.40s\n", TypeAsString(), name_);
1058 } else {
1059 OS::Print("\"");
1060 const char* c = name_;
1061 while (*c && (c - name_) <= 40) {
1062 if (*c != '\n')
1063 OS::Print("%c", *c);
1064 else
1065 OS::Print("\\n");
1066 ++c;
1067 }
1068 OS::Print("\"\n");
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001069 }
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001070 if (--max_depth == 0) return;
1071 Vector<HeapGraphEdge> ch = children();
1072 for (int i = 0; i < ch.length(); ++i) {
1073 HeapGraphEdge& edge = ch[i];
1074 switch (edge.type()) {
1075 case HeapGraphEdge::kContextVariable:
1076 OS::Print(" %*c #%s: ", indent, ' ', edge.name());
1077 break;
1078 case HeapGraphEdge::kElement:
1079 OS::Print(" %*c %d: ", indent, ' ', edge.index());
1080 break;
1081 case HeapGraphEdge::kInternal:
1082 OS::Print(" %*c $%s: ", indent, ' ', edge.name());
1083 break;
1084 case HeapGraphEdge::kProperty:
1085 OS::Print(" %*c %s: ", indent, ' ', edge.name());
1086 break;
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001087 case HeapGraphEdge::kHidden:
1088 OS::Print(" %*c $%d: ", indent, ' ', edge.index());
1089 break;
1090 case HeapGraphEdge::kShortcut:
1091 OS::Print(" %*c ^%s: ", indent, ' ', edge.name());
1092 break;
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001093 default:
1094 OS::Print("!!! unknown edge type: %d ", edge.type());
1095 }
1096 edge.to()->Print(max_depth, indent + 2);
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001097 }
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001098}
1099
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001100
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001101const char* HeapEntry::TypeAsString() {
1102 switch (type()) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001103 case kHidden: return "/hidden/";
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001104 case kObject: return "/object/";
1105 case kClosure: return "/closure/";
1106 case kString: return "/string/";
1107 case kCode: return "/code/";
1108 case kArray: return "/array/";
vegorov@chromium.org42841962010-10-18 11:18:59 +00001109 case kRegExp: return "/regexp/";
1110 case kHeapNumber: return "/number/";
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001111 case kNative: return "/native/";
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001112 default: return "???";
1113 }
1114}
1115
1116
1117int HeapEntry::EntriesSize(int entries_count,
1118 int children_count,
1119 int retainers_count) {
1120 return sizeof(HeapEntry) * entries_count // NOLINT
1121 + sizeof(HeapGraphEdge) * children_count // NOLINT
1122 + sizeof(HeapGraphEdge*) * retainers_count; // NOLINT
1123}
1124
1125
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001126class RetainedSizeCalculator {
1127 public:
1128 RetainedSizeCalculator()
1129 : retained_size_(0) {
1130 }
1131
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00001132 int retained_size() const { return retained_size_; }
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001133
1134 void Apply(HeapEntry** entry_ptr) {
1135 if ((*entry_ptr)->painted_reachable()) {
1136 retained_size_ += (*entry_ptr)->self_size();
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001137 }
1138 }
1139
1140 private:
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001141 int retained_size_;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001142};
1143
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001144void HeapEntry::CalculateExactRetainedSize() {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001145 // To calculate retained size, first we paint all reachable nodes in
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001146 // one color, then we paint (or re-paint) all nodes reachable from
1147 // other nodes with a different color. Then we sum up self sizes of
1148 // nodes painted with the first color.
1149 snapshot()->ClearPaint();
1150 PaintAllReachable();
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001151
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001152 List<HeapEntry*> list(10);
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001153 HeapEntry* root = snapshot()->root();
1154 if (this != root) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001155 list.Add(root);
1156 root->paint_reachable_from_others();
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001157 }
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001158 while (!list.is_empty()) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001159 HeapEntry* curr = list.RemoveLast();
1160 Vector<HeapGraphEdge> children = curr->children();
1161 for (int i = 0; i < children.length(); ++i) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001162 if (children[i].type() == HeapGraphEdge::kShortcut) continue;
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001163 HeapEntry* child = children[i].to();
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001164 if (child != this && child->not_painted_reachable_from_others()) {
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001165 list.Add(child);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001166 child->paint_reachable_from_others();
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001167 }
1168 }
1169 }
1170
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001171 RetainedSizeCalculator ret_size_calc;
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001172 snapshot()->IterateEntries(&ret_size_calc);
jkummerow@chromium.orge297f592011-06-08 10:05:15 +00001173 retained_size_ = ret_size_calc.retained_size();
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001174 ASSERT((retained_size_ & kExactRetainedSizeTag) == 0);
1175 retained_size_ |= kExactRetainedSizeTag;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001176}
1177
1178
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001179// It is very important to keep objects that form a heap snapshot
1180// as small as possible.
1181namespace { // Avoid littering the global namespace.
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001182
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001183template <size_t ptr_size> struct SnapshotSizeConstants;
1184
1185template <> struct SnapshotSizeConstants<4> {
1186 static const int kExpectedHeapGraphEdgeSize = 12;
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001187 static const int kExpectedHeapEntrySize = 36;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001188};
1189
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001190template <> struct SnapshotSizeConstants<8> {
1191 static const int kExpectedHeapGraphEdgeSize = 24;
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001192 static const int kExpectedHeapEntrySize = 48;
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001193};
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001194
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001195} // namespace
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001196
1197HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001198 HeapSnapshot::Type type,
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001199 const char* title,
1200 unsigned uid)
1201 : collection_(collection),
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001202 type_(type),
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001203 title_(title),
1204 uid_(uid),
ricow@chromium.orgeb7c1442010-10-04 08:54:21 +00001205 root_entry_(NULL),
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001206 gc_roots_entry_(NULL),
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001207 natives_root_entry_(NULL),
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001208 raw_entries_(NULL),
lrn@chromium.org7516f052011-03-30 08:52:27 +00001209 entries_sorted_(false) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001210 STATIC_ASSERT(
1211 sizeof(HeapGraphEdge) ==
1212 SnapshotSizeConstants<sizeof(void*)>::kExpectedHeapGraphEdgeSize); // NOLINT
1213 STATIC_ASSERT(
1214 sizeof(HeapEntry) ==
1215 SnapshotSizeConstants<sizeof(void*)>::kExpectedHeapEntrySize); // NOLINT
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001216}
1217
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001218HeapSnapshot::~HeapSnapshot() {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001219 DeleteArray(raw_entries_);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001220}
1221
1222
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001223void HeapSnapshot::Delete() {
1224 collection_->RemoveSnapshot(this);
1225 delete this;
1226}
1227
1228
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001229void HeapSnapshot::AllocateEntries(int entries_count,
1230 int children_count,
1231 int retainers_count) {
1232 ASSERT(raw_entries_ == NULL);
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001233 raw_entries_size_ =
1234 HeapEntry::EntriesSize(entries_count, children_count, retainers_count);
fschneider@chromium.org1805e212011-09-05 10:49:12 +00001235 raw_entries_ = NewArray<char>(raw_entries_size_);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001236}
1237
1238
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001239static void HeapEntryClearPaint(HeapEntry** entry_ptr) {
1240 (*entry_ptr)->clear_paint();
1241}
1242
1243void HeapSnapshot::ClearPaint() {
1244 entries_.Iterate(HeapEntryClearPaint);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001245}
1246
1247
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001248HeapEntry* HeapSnapshot::AddRootEntry(int children_count) {
1249 ASSERT(root_entry_ == NULL);
1250 return (root_entry_ = AddEntry(HeapEntry::kObject,
1251 "",
1252 HeapObjectsMap::kInternalRootObjectId,
1253 0,
1254 children_count,
1255 0));
1256}
1257
1258
1259HeapEntry* HeapSnapshot::AddGcRootsEntry(int children_count,
1260 int retainers_count) {
1261 ASSERT(gc_roots_entry_ == NULL);
1262 return (gc_roots_entry_ = AddEntry(HeapEntry::kObject,
1263 "(GC roots)",
1264 HeapObjectsMap::kGcRootsObjectId,
1265 0,
1266 children_count,
1267 retainers_count));
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001268}
1269
1270
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001271HeapEntry* HeapSnapshot::AddNativesRootEntry(int children_count,
1272 int retainers_count) {
1273 ASSERT(natives_root_entry_ == NULL);
1274 return (natives_root_entry_ = AddEntry(
1275 HeapEntry::kObject,
1276 "(Native objects)",
1277 HeapObjectsMap::kNativesRootObjectId,
1278 0,
1279 children_count,
1280 retainers_count));
1281}
1282
1283
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001284HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
1285 const char* name,
1286 uint64_t id,
1287 int size,
1288 int children_count,
1289 int retainers_count) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001290 HeapEntry* entry = GetNextEntryToInit();
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001291 entry->Init(this, type, name, id, size, children_count, retainers_count);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001292 return entry;
1293}
1294
1295
ager@chromium.orgbeb25712010-11-29 08:02:25 +00001296void HeapSnapshot::SetDominatorsToSelf() {
1297 for (int i = 0; i < entries_.length(); ++i) {
1298 HeapEntry* entry = entries_[i];
1299 if (entry->dominator() == NULL) entry->set_dominator(entry);
1300 }
1301}
1302
1303
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001304HeapEntry* HeapSnapshot::GetNextEntryToInit() {
1305 if (entries_.length() > 0) {
1306 HeapEntry* last_entry = entries_.last();
1307 entries_.Add(reinterpret_cast<HeapEntry*>(
1308 reinterpret_cast<char*>(last_entry) + last_entry->EntrySize()));
1309 } else {
1310 entries_.Add(reinterpret_cast<HeapEntry*>(raw_entries_));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001311 }
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001312 ASSERT(reinterpret_cast<char*>(entries_.last()) <
1313 (raw_entries_ + raw_entries_size_));
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001314 return entries_.last();
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001315}
1316
1317
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001318HeapEntry* HeapSnapshot::GetEntryById(uint64_t id) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001319 List<HeapEntry*>* entries_by_id = GetSortedEntriesList();
1320
1321 // Perform a binary search by id.
1322 int low = 0;
1323 int high = entries_by_id->length() - 1;
1324 while (low <= high) {
1325 int mid =
1326 (static_cast<unsigned int>(low) + static_cast<unsigned int>(high)) >> 1;
1327 uint64_t mid_id = entries_by_id->at(mid)->id();
1328 if (mid_id > id)
1329 high = mid - 1;
1330 else if (mid_id < id)
1331 low = mid + 1;
1332 else
1333 return entries_by_id->at(mid);
1334 }
1335 return NULL;
1336}
1337
1338
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001339template<class T>
1340static int SortByIds(const T* entry1_ptr,
1341 const T* entry2_ptr) {
1342 if ((*entry1_ptr)->id() == (*entry2_ptr)->id()) return 0;
1343 return (*entry1_ptr)->id() < (*entry2_ptr)->id() ? -1 : 1;
1344}
1345
1346List<HeapEntry*>* HeapSnapshot::GetSortedEntriesList() {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001347 if (!entries_sorted_) {
1348 entries_.Sort(SortByIds);
1349 entries_sorted_ = true;
1350 }
1351 return &entries_;
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001352}
1353
1354
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001355void HeapSnapshot::Print(int max_depth) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001356 root()->Print(max_depth, 0);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001357}
1358
1359
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001360// We split IDs on evens for embedder objects (see
1361// HeapObjectsMap::GenerateId) and odds for native objects.
1362const uint64_t HeapObjectsMap::kInternalRootObjectId = 1;
1363const uint64_t HeapObjectsMap::kGcRootsObjectId = 3;
1364const uint64_t HeapObjectsMap::kNativesRootObjectId = 5;
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001365// Increase kFirstAvailableObjectId if new 'special' objects appear.
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001366const uint64_t HeapObjectsMap::kFirstAvailableObjectId = 7;
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001367
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001368HeapObjectsMap::HeapObjectsMap()
1369 : initial_fill_mode_(true),
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001370 next_id_(kFirstAvailableObjectId),
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001371 entries_map_(AddressesMatch),
1372 entries_(new List<EntryInfo>()) { }
1373
1374
1375HeapObjectsMap::~HeapObjectsMap() {
1376 delete entries_;
1377}
1378
1379
1380void HeapObjectsMap::SnapshotGenerationFinished() {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001381 initial_fill_mode_ = false;
1382 RemoveDeadEntries();
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001383}
1384
1385
1386uint64_t HeapObjectsMap::FindObject(Address addr) {
1387 if (!initial_fill_mode_) {
1388 uint64_t existing = FindEntry(addr);
1389 if (existing != 0) return existing;
1390 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001391 uint64_t id = next_id_;
1392 next_id_ += 2;
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001393 AddEntry(addr, id);
1394 return id;
1395}
1396
1397
1398void HeapObjectsMap::MoveObject(Address from, Address to) {
1399 if (from == to) return;
1400 HashMap::Entry* entry = entries_map_.Lookup(from, AddressHash(from), false);
1401 if (entry != NULL) {
1402 void* value = entry->value;
1403 entries_map_.Remove(from, AddressHash(from));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001404 if (to != NULL) {
1405 entry = entries_map_.Lookup(to, AddressHash(to), true);
1406 // We can have an entry at the new location, it is OK, as GC can overwrite
1407 // dead objects with alive objects being moved.
1408 entry->value = value;
1409 }
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001410 }
1411}
1412
1413
1414void HeapObjectsMap::AddEntry(Address addr, uint64_t id) {
1415 HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), true);
1416 ASSERT(entry->value == NULL);
1417 entry->value = reinterpret_cast<void*>(entries_->length());
1418 entries_->Add(EntryInfo(id));
1419}
1420
1421
1422uint64_t HeapObjectsMap::FindEntry(Address addr) {
1423 HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), false);
1424 if (entry != NULL) {
1425 int entry_index =
1426 static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
1427 EntryInfo& entry_info = entries_->at(entry_index);
1428 entry_info.accessed = true;
1429 return entry_info.id;
1430 } else {
1431 return 0;
1432 }
1433}
1434
1435
1436void HeapObjectsMap::RemoveDeadEntries() {
1437 List<EntryInfo>* new_entries = new List<EntryInfo>();
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001438 List<void*> dead_entries;
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001439 for (HashMap::Entry* entry = entries_map_.Start();
1440 entry != NULL;
1441 entry = entries_map_.Next(entry)) {
1442 int entry_index =
1443 static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
1444 EntryInfo& entry_info = entries_->at(entry_index);
1445 if (entry_info.accessed) {
1446 entry->value = reinterpret_cast<void*>(new_entries->length());
1447 new_entries->Add(EntryInfo(entry_info.id, false));
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001448 } else {
1449 dead_entries.Add(entry->key);
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001450 }
1451 }
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001452 for (int i = 0; i < dead_entries.length(); ++i) {
1453 void* raw_entry = dead_entries[i];
1454 entries_map_.Remove(
1455 raw_entry, AddressHash(reinterpret_cast<Address>(raw_entry)));
1456 }
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001457 delete entries_;
1458 entries_ = new_entries;
1459}
1460
1461
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001462uint64_t HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) {
1463 uint64_t id = static_cast<uint64_t>(info->GetHash());
1464 const char* label = info->GetLabel();
1465 id ^= HashSequentialString(label, static_cast<int>(strlen(label)));
1466 intptr_t element_count = info->GetElementCount();
1467 if (element_count != -1)
1468 id ^= ComputeIntegerHash(static_cast<uint32_t>(element_count));
1469 return id << 1;
1470}
1471
1472
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001473HeapSnapshotsCollection::HeapSnapshotsCollection()
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001474 : is_tracking_objects_(false),
1475 snapshots_uids_(HeapSnapshotsMatch),
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001476 token_enumerator_(new TokenEnumerator()) {
1477}
1478
1479
1480static void DeleteHeapSnapshot(HeapSnapshot** snapshot_ptr) {
1481 delete *snapshot_ptr;
1482}
1483
1484
1485HeapSnapshotsCollection::~HeapSnapshotsCollection() {
1486 delete token_enumerator_;
1487 snapshots_.Iterate(DeleteHeapSnapshot);
1488}
1489
1490
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001491HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(HeapSnapshot::Type type,
1492 const char* name,
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001493 unsigned uid) {
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001494 is_tracking_objects_ = true; // Start watching for heap objects moves.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001495 return new HeapSnapshot(this, type, name, uid);
1496}
1497
1498
1499void HeapSnapshotsCollection::SnapshotGenerationFinished(
1500 HeapSnapshot* snapshot) {
1501 ids_.SnapshotGenerationFinished();
1502 if (snapshot != NULL) {
1503 snapshots_.Add(snapshot);
1504 HashMap::Entry* entry =
1505 snapshots_uids_.Lookup(reinterpret_cast<void*>(snapshot->uid()),
1506 static_cast<uint32_t>(snapshot->uid()),
1507 true);
1508 ASSERT(entry->value == NULL);
1509 entry->value = snapshot;
1510 }
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001511}
1512
1513
1514HeapSnapshot* HeapSnapshotsCollection::GetSnapshot(unsigned uid) {
1515 HashMap::Entry* entry = snapshots_uids_.Lookup(reinterpret_cast<void*>(uid),
1516 static_cast<uint32_t>(uid),
1517 false);
1518 return entry != NULL ? reinterpret_cast<HeapSnapshot*>(entry->value) : NULL;
1519}
1520
1521
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001522void HeapSnapshotsCollection::RemoveSnapshot(HeapSnapshot* snapshot) {
1523 snapshots_.RemoveElement(snapshot);
1524 unsigned uid = snapshot->uid();
1525 snapshots_uids_.Remove(reinterpret_cast<void*>(uid),
1526 static_cast<uint32_t>(uid));
1527}
1528
1529
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001530Handle<HeapObject> HeapSnapshotsCollection::FindHeapObjectById(uint64_t id) {
1531 // First perform a full GC in order to avoid dead objects.
1532 HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask);
1533 AssertNoAllocation no_allocation;
1534 HeapObject* object = NULL;
1535 HeapIterator iterator(HeapIterator::kFilterUnreachable);
1536 // Make sure that object with the given id is still reachable.
1537 for (HeapObject* obj = iterator.next();
1538 obj != NULL;
1539 obj = iterator.next()) {
1540 if (ids_.FindObject(obj->address()) == id) {
1541 ASSERT(object == NULL);
1542 object = obj;
1543 // Can't break -- kFilterUnreachable requires full heap traversal.
1544 }
1545 }
1546 return object != NULL ? Handle<HeapObject>(object) : Handle<HeapObject>();
1547}
1548
1549
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001550HeapEntry *const HeapEntriesMap::kHeapEntryPlaceholder =
1551 reinterpret_cast<HeapEntry*>(1);
1552
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001553HeapEntriesMap::HeapEntriesMap()
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001554 : entries_(HeapThingsMatch),
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001555 entries_count_(0),
1556 total_children_count_(0),
1557 total_retainers_count_(0) {
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001558}
1559
1560
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001561HeapEntriesMap::~HeapEntriesMap() {
1562 for (HashMap::Entry* p = entries_.Start(); p != NULL; p = entries_.Next(p)) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001563 delete reinterpret_cast<EntryInfo*>(p->value);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001564 }
1565}
1566
1567
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001568void HeapEntriesMap::AllocateEntries() {
1569 for (HashMap::Entry* p = entries_.Start();
1570 p != NULL;
1571 p = entries_.Next(p)) {
1572 EntryInfo* entry_info = reinterpret_cast<EntryInfo*>(p->value);
1573 entry_info->entry = entry_info->allocator->AllocateEntry(
1574 p->key,
1575 entry_info->children_count,
1576 entry_info->retainers_count);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001577 ASSERT(entry_info->entry != NULL);
1578 ASSERT(entry_info->entry != kHeapEntryPlaceholder);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001579 entry_info->children_count = 0;
1580 entry_info->retainers_count = 0;
1581 }
1582}
1583
1584
1585HeapEntry* HeapEntriesMap::Map(HeapThing thing) {
1586 HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), false);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001587 if (cache_entry != NULL) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001588 EntryInfo* entry_info = reinterpret_cast<EntryInfo*>(cache_entry->value);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001589 return entry_info->entry;
1590 } else {
1591 return NULL;
1592 }
1593}
1594
1595
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001596void HeapEntriesMap::Pair(
1597 HeapThing thing, HeapEntriesAllocator* allocator, HeapEntry* entry) {
1598 HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), true);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001599 ASSERT(cache_entry->value == NULL);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001600 cache_entry->value = new EntryInfo(entry, allocator);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001601 ++entries_count_;
1602}
1603
1604
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001605void HeapEntriesMap::CountReference(HeapThing from, HeapThing to,
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001606 int* prev_children_count,
1607 int* prev_retainers_count) {
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001608 HashMap::Entry* from_cache_entry = entries_.Lookup(from, Hash(from), false);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001609 HashMap::Entry* to_cache_entry = entries_.Lookup(to, Hash(to), false);
1610 ASSERT(from_cache_entry != NULL);
1611 ASSERT(to_cache_entry != NULL);
1612 EntryInfo* from_entry_info =
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001613 reinterpret_cast<EntryInfo*>(from_cache_entry->value);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001614 EntryInfo* to_entry_info =
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001615 reinterpret_cast<EntryInfo*>(to_cache_entry->value);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001616 if (prev_children_count)
1617 *prev_children_count = from_entry_info->children_count;
1618 if (prev_retainers_count)
1619 *prev_retainers_count = to_entry_info->retainers_count;
1620 ++from_entry_info->children_count;
1621 ++to_entry_info->retainers_count;
1622 ++total_children_count_;
1623 ++total_retainers_count_;
1624}
1625
1626
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001627HeapObjectsSet::HeapObjectsSet()
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001628 : entries_(HeapEntriesMap::HeapThingsMatch) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001629}
1630
1631
1632void HeapObjectsSet::Clear() {
1633 entries_.Clear();
1634}
1635
1636
1637bool HeapObjectsSet::Contains(Object* obj) {
1638 if (!obj->IsHeapObject()) return false;
1639 HeapObject* object = HeapObject::cast(obj);
1640 HashMap::Entry* cache_entry =
1641 entries_.Lookup(object, HeapEntriesMap::Hash(object), false);
1642 return cache_entry != NULL;
1643}
1644
1645
1646void HeapObjectsSet::Insert(Object* obj) {
1647 if (!obj->IsHeapObject()) return;
1648 HeapObject* object = HeapObject::cast(obj);
1649 HashMap::Entry* cache_entry =
1650 entries_.Lookup(object, HeapEntriesMap::Hash(object), true);
1651 if (cache_entry->value == NULL) {
1652 cache_entry->value = HeapEntriesMap::kHeapEntryPlaceholder;
1653 }
1654}
1655
1656
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00001657const char* HeapObjectsSet::GetTag(Object* obj) {
1658 HeapObject* object = HeapObject::cast(obj);
1659 HashMap::Entry* cache_entry =
1660 entries_.Lookup(object, HeapEntriesMap::Hash(object), false);
1661 if (cache_entry != NULL
1662 && cache_entry->value != HeapEntriesMap::kHeapEntryPlaceholder) {
1663 return reinterpret_cast<const char*>(cache_entry->value);
1664 } else {
1665 return NULL;
1666 }
1667}
1668
1669
1670void HeapObjectsSet::SetTag(Object* obj, const char* tag) {
1671 if (!obj->IsHeapObject()) return;
1672 HeapObject* object = HeapObject::cast(obj);
1673 HashMap::Entry* cache_entry =
1674 entries_.Lookup(object, HeapEntriesMap::Hash(object), true);
1675 cache_entry->value = const_cast<char*>(tag);
1676}
1677
1678
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001679HeapObject *const V8HeapExplorer::kInternalRootObject =
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001680 reinterpret_cast<HeapObject*>(
1681 static_cast<intptr_t>(HeapObjectsMap::kInternalRootObjectId));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001682HeapObject *const V8HeapExplorer::kGcRootsObject =
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001683 reinterpret_cast<HeapObject*>(
1684 static_cast<intptr_t>(HeapObjectsMap::kGcRootsObjectId));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001685
1686
1687V8HeapExplorer::V8HeapExplorer(
1688 HeapSnapshot* snapshot,
1689 SnapshottingProgressReportingInterface* progress)
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001690 : heap_(Isolate::Current()->heap()),
1691 snapshot_(snapshot),
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001692 collection_(snapshot_->collection()),
1693 progress_(progress),
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001694 filler_(NULL) {
1695}
1696
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001697
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001698V8HeapExplorer::~V8HeapExplorer() {
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001699}
1700
1701
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001702HeapEntry* V8HeapExplorer::AllocateEntry(
1703 HeapThing ptr, int children_count, int retainers_count) {
1704 return AddEntry(
1705 reinterpret_cast<HeapObject*>(ptr), children_count, retainers_count);
1706}
1707
1708
1709HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
1710 int children_count,
1711 int retainers_count) {
1712 if (object == kInternalRootObject) {
1713 ASSERT(retainers_count == 0);
1714 return snapshot_->AddRootEntry(children_count);
1715 } else if (object == kGcRootsObject) {
1716 return snapshot_->AddGcRootsEntry(children_count, retainers_count);
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00001717 } else if (object->IsJSGlobalObject()) {
1718 const char* tag = objects_tags_.GetTag(object);
1719 const char* name = collection_->names()->GetName(
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00001720 GetConstructorName(JSObject::cast(object)));
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00001721 if (tag != NULL) {
1722 name = collection_->names()->GetFormatted("%s / %s", name, tag);
1723 }
1724 return AddEntry(object,
1725 HeapEntry::kObject,
1726 name,
1727 children_count,
1728 retainers_count);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001729 } else if (object->IsJSFunction()) {
1730 JSFunction* func = JSFunction::cast(object);
1731 SharedFunctionInfo* shared = func->shared();
1732 return AddEntry(object,
1733 HeapEntry::kClosure,
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001734 collection_->names()->GetName(String::cast(shared->name())),
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001735 children_count,
1736 retainers_count);
1737 } else if (object->IsJSRegExp()) {
1738 JSRegExp* re = JSRegExp::cast(object);
1739 return AddEntry(object,
1740 HeapEntry::kRegExp,
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001741 collection_->names()->GetName(re->Pattern()),
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001742 children_count,
1743 retainers_count);
1744 } else if (object->IsJSObject()) {
1745 return AddEntry(object,
1746 HeapEntry::kObject,
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001747 collection_->names()->GetName(
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00001748 GetConstructorName(JSObject::cast(object))),
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001749 children_count,
1750 retainers_count);
1751 } else if (object->IsString()) {
1752 return AddEntry(object,
1753 HeapEntry::kString,
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001754 collection_->names()->GetName(String::cast(object)),
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001755 children_count,
1756 retainers_count);
1757 } else if (object->IsCode()) {
1758 return AddEntry(object,
1759 HeapEntry::kCode,
1760 "",
1761 children_count,
1762 retainers_count);
1763 } else if (object->IsSharedFunctionInfo()) {
1764 SharedFunctionInfo* shared = SharedFunctionInfo::cast(object);
1765 return AddEntry(object,
1766 HeapEntry::kCode,
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001767 collection_->names()->GetName(String::cast(shared->name())),
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001768 children_count,
1769 retainers_count);
1770 } else if (object->IsScript()) {
1771 Script* script = Script::cast(object);
1772 return AddEntry(object,
1773 HeapEntry::kCode,
1774 script->name()->IsString() ?
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001775 collection_->names()->GetName(
1776 String::cast(script->name()))
1777 : "",
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001778 children_count,
1779 retainers_count);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001780 } else if (object->IsFixedArray() ||
1781 object->IsFixedDoubleArray() ||
1782 object->IsByteArray() ||
1783 object->IsExternalArray()) {
1784 const char* tag = objects_tags_.GetTag(object);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001785 return AddEntry(object,
1786 HeapEntry::kArray,
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001787 tag != NULL ? tag : "",
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001788 children_count,
1789 retainers_count);
1790 } else if (object->IsHeapNumber()) {
1791 return AddEntry(object,
1792 HeapEntry::kHeapNumber,
1793 "number",
1794 children_count,
1795 retainers_count);
1796 }
1797 return AddEntry(object,
1798 HeapEntry::kHidden,
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001799 GetSystemEntryName(object),
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001800 children_count,
1801 retainers_count);
1802}
1803
1804
1805HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
1806 HeapEntry::Type type,
1807 const char* name,
1808 int children_count,
1809 int retainers_count) {
1810 return snapshot_->AddEntry(type,
1811 name,
1812 collection_->GetObjectId(object->address()),
1813 object->Size(),
1814 children_count,
1815 retainers_count);
1816}
1817
1818
1819void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001820 filler->AddEntry(kInternalRootObject, this);
1821 filler->AddEntry(kGcRootsObject, this);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001822}
1823
1824
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001825const char* V8HeapExplorer::GetSystemEntryName(HeapObject* object) {
1826 switch (object->map()->instance_type()) {
1827 case MAP_TYPE: return "system / Map";
1828 case JS_GLOBAL_PROPERTY_CELL_TYPE: return "system / JSGlobalPropertyCell";
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001829 case FOREIGN_TYPE: return "system / Foreign";
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001830 case ODDBALL_TYPE: return "system / Oddball";
1831#define MAKE_STRUCT_CASE(NAME, Name, name) \
1832 case NAME##_TYPE: return "system / "#Name;
1833 STRUCT_LIST(MAKE_STRUCT_CASE)
1834#undef MAKE_STRUCT_CASE
1835 default: return "system";
1836 }
1837}
1838
1839
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001840int V8HeapExplorer::EstimateObjectsCount(HeapIterator* iterator) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001841 int objects_count = 0;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001842 for (HeapObject* obj = iterator->next();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001843 obj != NULL;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001844 obj = iterator->next()) {
1845 objects_count++;
1846 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001847 return objects_count;
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001848}
1849
1850
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001851class IndexedReferencesExtractor : public ObjectVisitor {
1852 public:
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001853 IndexedReferencesExtractor(V8HeapExplorer* generator,
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001854 HeapObject* parent_obj,
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001855 HeapEntry* parent_entry)
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001856 : generator_(generator),
1857 parent_obj_(parent_obj),
1858 parent_(parent_entry),
1859 next_index_(1) {
1860 }
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001861 void VisitPointers(Object** start, Object** end) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001862 for (Object** p = start; p < end; p++) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001863 if (CheckVisitedAndUnmark(p)) continue;
1864 generator_->SetHiddenReference(parent_obj_, parent_, next_index_++, *p);
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001865 }
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001866 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001867 static void MarkVisitedField(HeapObject* obj, int offset) {
1868 if (offset < 0) return;
1869 Address field = obj->address() + offset;
1870 ASSERT(!Memory::Object_at(field)->IsFailure());
1871 ASSERT(Memory::Object_at(field)->IsHeapObject());
1872 *field |= kFailureTag;
1873 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00001874
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001875 private:
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001876 bool CheckVisitedAndUnmark(Object** field) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001877 if ((*field)->IsFailure()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001878 intptr_t untagged = reinterpret_cast<intptr_t>(*field) & ~kFailureTagMask;
1879 *field = reinterpret_cast<Object*>(untagged | kHeapObjectTag);
1880 ASSERT((*field)->IsHeapObject());
1881 return true;
1882 }
1883 return false;
1884 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001885 V8HeapExplorer* generator_;
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001886 HeapObject* parent_obj_;
1887 HeapEntry* parent_;
1888 int next_index_;
1889};
1890
1891
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001892void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001893 HeapEntry* entry = GetEntry(obj);
1894 if (entry == NULL) return; // No interest in this object.
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001895
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001896 bool extract_indexed_refs = true;
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001897 if (obj->IsJSGlobalProxy()) {
1898 // We need to reference JS global objects from snapshot's root.
1899 // We use JSGlobalProxy because this is what embedder (e.g. browser)
1900 // uses for the global object.
1901 JSGlobalProxy* proxy = JSGlobalProxy::cast(obj);
1902 SetRootShortcutReference(proxy->map()->prototype());
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001903 } else if (obj->IsJSObject()) {
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001904 JSObject* js_obj = JSObject::cast(obj);
1905 ExtractClosureReferences(js_obj, entry);
1906 ExtractPropertyReferences(js_obj, entry);
1907 ExtractElementReferences(js_obj, entry);
vegorov@chromium.org42841962010-10-18 11:18:59 +00001908 ExtractInternalReferences(js_obj, entry);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001909 SetPropertyReference(
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001910 obj, entry, heap_->Proto_symbol(), js_obj->GetPrototype());
vegorov@chromium.org42841962010-10-18 11:18:59 +00001911 if (obj->IsJSFunction()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001912 JSFunction* js_fun = JSFunction::cast(js_obj);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001913 Object* proto_or_map = js_fun->prototype_or_initial_map();
1914 if (!proto_or_map->IsTheHole()) {
1915 if (!proto_or_map->IsMap()) {
1916 SetPropertyReference(
1917 obj, entry,
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001918 heap_->prototype_symbol(), proto_or_map,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001919 JSFunction::kPrototypeOrInitialMapOffset);
1920 } else {
1921 SetPropertyReference(
1922 obj, entry,
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001923 heap_->prototype_symbol(), js_fun->prototype());
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001924 }
vegorov@chromium.org42841962010-10-18 11:18:59 +00001925 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001926 SetInternalReference(js_fun, entry,
1927 "shared", js_fun->shared(),
1928 JSFunction::kSharedFunctionInfoOffset);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001929 TagObject(js_fun->unchecked_context(), "(context)");
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001930 SetInternalReference(js_fun, entry,
1931 "context", js_fun->unchecked_context(),
1932 JSFunction::kContextOffset);
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001933 TagObject(js_fun->literals_or_bindings(),
1934 "(function literals_or_bindings)");
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001935 SetInternalReference(js_fun, entry,
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001936 "literals_or_bindings",
1937 js_fun->literals_or_bindings(),
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001938 JSFunction::kLiteralsOffset);
vegorov@chromium.org42841962010-10-18 11:18:59 +00001939 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001940 TagObject(js_obj->properties(), "(object properties)");
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001941 SetInternalReference(obj, entry,
1942 "properties", js_obj->properties(),
1943 JSObject::kPropertiesOffset);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001944 TagObject(js_obj->elements(), "(object elements)");
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001945 SetInternalReference(obj, entry,
1946 "elements", js_obj->elements(),
1947 JSObject::kElementsOffset);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001948 } else if (obj->IsString()) {
1949 if (obj->IsConsString()) {
1950 ConsString* cs = ConsString::cast(obj);
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001951 SetInternalReference(obj, entry, 1, cs->first());
1952 SetInternalReference(obj, entry, 2, cs->second());
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001953 }
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00001954 if (obj->IsSlicedString()) {
1955 SlicedString* ss = SlicedString::cast(obj);
1956 SetInternalReference(obj, entry, "parent", ss->parent());
1957 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001958 extract_indexed_refs = false;
1959 } else if (obj->IsGlobalContext()) {
1960 Context* context = Context::cast(obj);
1961 TagObject(context->jsfunction_result_caches(),
1962 "(context func. result caches)");
1963 TagObject(context->normalized_map_cache(), "(context norm. map cache)");
1964 TagObject(context->runtime_context(), "(runtime context)");
1965 TagObject(context->map_cache(), "(context map cache)");
1966 TagObject(context->data(), "(context data)");
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001967 } else if (obj->IsMap()) {
1968 Map* map = Map::cast(obj);
1969 SetInternalReference(obj, entry,
1970 "prototype", map->prototype(), Map::kPrototypeOffset);
1971 SetInternalReference(obj, entry,
1972 "constructor", map->constructor(),
1973 Map::kConstructorOffset);
danno@chromium.org40cb8782011-05-25 07:58:50 +00001974 if (!map->instance_descriptors()->IsEmpty()) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001975 TagObject(map->instance_descriptors(), "(map descriptors)");
danno@chromium.org40cb8782011-05-25 07:58:50 +00001976 SetInternalReference(obj, entry,
1977 "descriptors", map->instance_descriptors(),
1978 Map::kInstanceDescriptorsOrBitField3Offset);
1979 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001980 if (map->prototype_transitions() != heap_->empty_fixed_array()) {
1981 TagObject(map->prototype_transitions(), "(prototype transitions)");
1982 SetInternalReference(obj,
1983 entry,
1984 "prototype_transitions",
1985 map->prototype_transitions(),
1986 Map::kPrototypeTransitionsOffset);
1987 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001988 SetInternalReference(obj, entry,
1989 "code_cache", map->code_cache(),
1990 Map::kCodeCacheOffset);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001991 } else if (obj->IsSharedFunctionInfo()) {
1992 SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
1993 SetInternalReference(obj, entry,
1994 "name", shared->name(),
1995 SharedFunctionInfo::kNameOffset);
1996 SetInternalReference(obj, entry,
1997 "code", shared->unchecked_code(),
1998 SharedFunctionInfo::kCodeOffset);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001999 TagObject(shared->scope_info(), "(function scope info)");
2000 SetInternalReference(obj, entry,
2001 "scope_info", shared->scope_info(),
2002 SharedFunctionInfo::kScopeInfoOffset);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002003 SetInternalReference(obj, entry,
2004 "instance_class_name", shared->instance_class_name(),
2005 SharedFunctionInfo::kInstanceClassNameOffset);
2006 SetInternalReference(obj, entry,
2007 "script", shared->script(),
2008 SharedFunctionInfo::kScriptOffset);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002009 } else if (obj->IsScript()) {
2010 Script* script = Script::cast(obj);
2011 SetInternalReference(obj, entry,
2012 "source", script->source(),
2013 Script::kSourceOffset);
2014 SetInternalReference(obj, entry,
2015 "name", script->name(),
2016 Script::kNameOffset);
2017 SetInternalReference(obj, entry,
2018 "data", script->data(),
2019 Script::kDataOffset);
2020 SetInternalReference(obj, entry,
2021 "context_data", script->context_data(),
2022 Script::kContextOffset);
2023 TagObject(script->line_ends(), "(script line ends)");
2024 SetInternalReference(obj, entry,
2025 "line_ends", script->line_ends(),
2026 Script::kLineEndsOffset);
2027 } else if (obj->IsDescriptorArray()) {
2028 DescriptorArray* desc_array = DescriptorArray::cast(obj);
2029 if (desc_array->length() > DescriptorArray::kContentArrayIndex) {
2030 Object* content_array =
2031 desc_array->get(DescriptorArray::kContentArrayIndex);
2032 TagObject(content_array, "(map descriptor content)");
2033 SetInternalReference(obj, entry,
2034 "content", content_array,
2035 FixedArray::OffsetOfElementAt(
2036 DescriptorArray::kContentArrayIndex));
2037 }
2038 } else if (obj->IsCodeCache()) {
2039 CodeCache* code_cache = CodeCache::cast(obj);
2040 TagObject(code_cache->default_cache(), "(default code cache)");
2041 SetInternalReference(obj, entry,
2042 "default_cache", code_cache->default_cache(),
2043 CodeCache::kDefaultCacheOffset);
2044 TagObject(code_cache->normal_type_cache(), "(code type cache)");
2045 SetInternalReference(obj, entry,
2046 "type_cache", code_cache->normal_type_cache(),
2047 CodeCache::kNormalTypeCacheOffset);
2048 } else if (obj->IsCode()) {
2049 Code* code = Code::cast(obj);
2050 TagObject(code->unchecked_relocation_info(), "(code relocation info)");
2051 TagObject(code->unchecked_deoptimization_data(), "(code deopt data)");
2052 }
2053 if (extract_indexed_refs) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002054 SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002055 IndexedReferencesExtractor refs_extractor(this, obj, entry);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002056 obj->Iterate(&refs_extractor);
2057 }
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002058}
2059
2060
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002061void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj,
2062 HeapEntry* entry) {
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002063 if (js_obj->IsJSFunction()) {
2064 HandleScope hs;
2065 JSFunction* func = JSFunction::cast(js_obj);
2066 Context* context = func->context();
danno@chromium.org40cb8782011-05-25 07:58:50 +00002067 ZoneScope zscope(Isolate::Current(), DELETE_ON_EXIT);
ager@chromium.orgb5737492010-07-15 09:29:43 +00002068 SerializedScopeInfo* serialized_scope_info =
2069 context->closure()->shared()->scope_info();
2070 ScopeInfo<ZoneListAllocationPolicy> zone_scope_info(serialized_scope_info);
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00002071 int locals_number = zone_scope_info.NumberOfLocals();
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002072 for (int i = 0; i < locals_number; ++i) {
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00002073 String* local_name = *zone_scope_info.LocalName(i);
ager@chromium.orgb5737492010-07-15 09:29:43 +00002074 int idx = serialized_scope_info->ContextSlotIndex(local_name, NULL);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002075 if (idx >= 0 && idx < context->length()) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002076 SetClosureReference(js_obj, entry, local_name, context->get(idx));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002077 }
2078 }
2079 }
2080}
2081
2082
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002083void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj,
2084 HeapEntry* entry) {
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002085 if (js_obj->HasFastProperties()) {
2086 DescriptorArray* descs = js_obj->map()->instance_descriptors();
2087 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2088 switch (descs->GetType(i)) {
2089 case FIELD: {
2090 int index = descs->GetFieldIndex(i);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002091 if (index < js_obj->map()->inobject_properties()) {
2092 SetPropertyReference(
2093 js_obj, entry,
2094 descs->GetKey(i), js_obj->InObjectPropertyAt(index),
2095 js_obj->GetInObjectPropertyOffset(index));
2096 } else {
2097 SetPropertyReference(
2098 js_obj, entry,
2099 descs->GetKey(i), js_obj->FastPropertyAt(index));
2100 }
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002101 break;
2102 }
2103 case CONSTANT_FUNCTION:
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002104 SetPropertyReference(
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002105 js_obj, entry,
2106 descs->GetKey(i), descs->GetConstantFunction(i));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002107 break;
2108 default: ;
2109 }
2110 }
2111 } else {
2112 StringDictionary* dictionary = js_obj->property_dictionary();
2113 int length = dictionary->Capacity();
2114 for (int i = 0; i < length; ++i) {
2115 Object* k = dictionary->KeyAt(i);
2116 if (dictionary->IsKey(k)) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002117 Object* target = dictionary->ValueAt(i);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002118 SetPropertyReference(
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002119 js_obj, entry, String::cast(k), target);
2120 // We assume that global objects can only have slow properties.
2121 if (target->IsJSGlobalPropertyCell()) {
2122 SetPropertyShortcutReference(js_obj,
2123 entry,
2124 String::cast(k),
2125 JSGlobalPropertyCell::cast(
2126 target)->value());
2127 }
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002128 }
2129 }
2130 }
2131}
2132
2133
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002134void V8HeapExplorer::ExtractElementReferences(JSObject* js_obj,
2135 HeapEntry* entry) {
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002136 if (js_obj->HasFastElements()) {
2137 FixedArray* elements = FixedArray::cast(js_obj->elements());
2138 int length = js_obj->IsJSArray() ?
2139 Smi::cast(JSArray::cast(js_obj)->length())->value() :
2140 elements->length();
2141 for (int i = 0; i < length; ++i) {
2142 if (!elements->get(i)->IsTheHole()) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002143 SetElementReference(js_obj, entry, i, elements->get(i));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002144 }
2145 }
2146 } else if (js_obj->HasDictionaryElements()) {
2147 NumberDictionary* dictionary = js_obj->element_dictionary();
2148 int length = dictionary->Capacity();
2149 for (int i = 0; i < length; ++i) {
2150 Object* k = dictionary->KeyAt(i);
2151 if (dictionary->IsKey(k)) {
2152 ASSERT(k->IsNumber());
2153 uint32_t index = static_cast<uint32_t>(k->Number());
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002154 SetElementReference(js_obj, entry, index, dictionary->ValueAt(i));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002155 }
2156 }
2157 }
2158}
2159
ricow@chromium.org4980dff2010-07-19 08:33:45 +00002160
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002161void V8HeapExplorer::ExtractInternalReferences(JSObject* js_obj,
2162 HeapEntry* entry) {
vegorov@chromium.org42841962010-10-18 11:18:59 +00002163 int length = js_obj->GetInternalFieldCount();
2164 for (int i = 0; i < length; ++i) {
2165 Object* o = js_obj->GetInternalField(i);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002166 SetInternalReference(
2167 js_obj, entry, i, o, js_obj->GetInternalFieldOffset(i));
vegorov@chromium.org42841962010-10-18 11:18:59 +00002168 }
2169}
2170
2171
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00002172String* V8HeapExplorer::GetConstructorName(JSObject* object) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002173 Heap* heap = object->GetHeap();
2174 if (object->IsJSFunction()) return heap->closure_symbol();
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00002175 String* constructor_name = object->constructor_name();
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002176 if (constructor_name == heap->Object_symbol()) {
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00002177 // Look up an immediate "constructor" property, if it is a function,
2178 // return its name. This is for instances of binding objects, which
2179 // have prototype constructor type "Object".
2180 Object* constructor_prop = NULL;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002181 LookupResult result(heap->isolate());
2182 object->LocalLookupRealNamedProperty(heap->constructor_symbol(), &result);
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00002183 if (result.IsProperty()) {
2184 constructor_prop = result.GetLazyValue();
2185 }
2186 if (constructor_prop->IsJSFunction()) {
2187 Object* maybe_name = JSFunction::cast(constructor_prop)->shared()->name();
2188 if (maybe_name->IsString()) {
2189 String* name = String::cast(maybe_name);
2190 if (name->length() > 0) return name;
2191 }
2192 }
2193 }
2194 return object->constructor_name();
2195}
2196
2197
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002198HeapEntry* V8HeapExplorer::GetEntry(Object* obj) {
2199 if (!obj->IsHeapObject()) return NULL;
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002200 return filler_->FindOrAddEntry(obj, this);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002201}
2202
2203
2204class RootsReferencesExtractor : public ObjectVisitor {
2205 public:
2206 explicit RootsReferencesExtractor(V8HeapExplorer* explorer)
2207 : explorer_(explorer) {
2208 }
2209 void VisitPointers(Object** start, Object** end) {
2210 for (Object** p = start; p < end; p++) explorer_->SetGcRootsReference(*p);
2211 }
2212 private:
2213 V8HeapExplorer* explorer_;
2214};
2215
2216
2217bool V8HeapExplorer::IterateAndExtractReferences(
2218 SnapshotFillerInterface* filler) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002219 HeapIterator iterator(HeapIterator::kFilterUnreachable);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002220
2221 filler_ = filler;
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002222 bool interrupted = false;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002223
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002224 // Heap iteration with filtering must be finished in any case.
2225 for (HeapObject* obj = iterator.next();
2226 obj != NULL;
2227 obj = iterator.next(), progress_->ProgressStep()) {
2228 if (!interrupted) {
2229 ExtractReferences(obj);
2230 if (!progress_->ProgressReport(false)) interrupted = true;
2231 }
2232 }
2233 if (interrupted) {
2234 filler_ = NULL;
2235 return false;
2236 }
2237 SetRootGcRootsReference();
2238 RootsReferencesExtractor extractor(this);
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002239 heap_->IterateRoots(&extractor, VISIT_ALL);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002240 filler_ = NULL;
2241 return progress_->ProgressReport(false);
2242}
2243
2244
2245void V8HeapExplorer::SetClosureReference(HeapObject* parent_obj,
2246 HeapEntry* parent_entry,
2247 String* reference_name,
2248 Object* child_obj) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002249 HeapEntry* child_entry = GetEntry(child_obj);
2250 if (child_entry != NULL) {
2251 filler_->SetNamedReference(HeapGraphEdge::kContextVariable,
2252 parent_obj,
2253 parent_entry,
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002254 collection_->names()->GetName(reference_name),
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002255 child_obj,
2256 child_entry);
2257 }
2258}
2259
2260
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002261void V8HeapExplorer::SetElementReference(HeapObject* parent_obj,
2262 HeapEntry* parent_entry,
2263 int index,
2264 Object* child_obj) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002265 HeapEntry* child_entry = GetEntry(child_obj);
2266 if (child_entry != NULL) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002267 filler_->SetIndexedReference(HeapGraphEdge::kElement,
2268 parent_obj,
2269 parent_entry,
2270 index,
2271 child_obj,
2272 child_entry);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002273 }
2274}
2275
2276
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002277void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
2278 HeapEntry* parent_entry,
2279 const char* reference_name,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002280 Object* child_obj,
2281 int field_offset) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002282 HeapEntry* child_entry = GetEntry(child_obj);
2283 if (child_entry != NULL) {
2284 filler_->SetNamedReference(HeapGraphEdge::kInternal,
2285 parent_obj,
2286 parent_entry,
2287 reference_name,
2288 child_obj,
2289 child_entry);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002290 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002291 }
2292}
2293
2294
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002295void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
2296 HeapEntry* parent_entry,
2297 int index,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002298 Object* child_obj,
2299 int field_offset) {
vegorov@chromium.org42841962010-10-18 11:18:59 +00002300 HeapEntry* child_entry = GetEntry(child_obj);
2301 if (child_entry != NULL) {
2302 filler_->SetNamedReference(HeapGraphEdge::kInternal,
2303 parent_obj,
2304 parent_entry,
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002305 collection_->names()->GetName(index),
vegorov@chromium.org42841962010-10-18 11:18:59 +00002306 child_obj,
2307 child_entry);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002308 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002309 }
2310}
2311
2312
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002313void V8HeapExplorer::SetHiddenReference(HeapObject* parent_obj,
2314 HeapEntry* parent_entry,
2315 int index,
2316 Object* child_obj) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002317 HeapEntry* child_entry = GetEntry(child_obj);
2318 if (child_entry != NULL) {
2319 filler_->SetIndexedReference(HeapGraphEdge::kHidden,
2320 parent_obj,
2321 parent_entry,
2322 index,
2323 child_obj,
2324 child_entry);
vegorov@chromium.org42841962010-10-18 11:18:59 +00002325 }
2326}
2327
2328
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002329void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj,
2330 HeapEntry* parent_entry,
2331 String* reference_name,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002332 Object* child_obj,
2333 int field_offset) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002334 HeapEntry* child_entry = GetEntry(child_obj);
2335 if (child_entry != NULL) {
vegorov@chromium.org42841962010-10-18 11:18:59 +00002336 HeapGraphEdge::Type type = reference_name->length() > 0 ?
2337 HeapGraphEdge::kProperty : HeapGraphEdge::kInternal;
2338 filler_->SetNamedReference(type,
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002339 parent_obj,
2340 parent_entry,
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002341 collection_->names()->GetName(reference_name),
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002342 child_obj,
2343 child_entry);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002344 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002345 }
2346}
2347
2348
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002349void V8HeapExplorer::SetPropertyShortcutReference(HeapObject* parent_obj,
2350 HeapEntry* parent_entry,
2351 String* reference_name,
2352 Object* child_obj) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002353 HeapEntry* child_entry = GetEntry(child_obj);
2354 if (child_entry != NULL) {
2355 filler_->SetNamedReference(HeapGraphEdge::kShortcut,
2356 parent_obj,
2357 parent_entry,
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002358 collection_->names()->GetName(reference_name),
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002359 child_obj,
2360 child_entry);
2361 }
2362}
2363
2364
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002365void V8HeapExplorer::SetRootGcRootsReference() {
2366 filler_->SetIndexedAutoIndexReference(
2367 HeapGraphEdge::kElement,
2368 kInternalRootObject, snapshot_->root(),
2369 kGcRootsObject, snapshot_->gc_roots());
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002370}
2371
2372
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002373void V8HeapExplorer::SetRootShortcutReference(Object* child_obj) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002374 HeapEntry* child_entry = GetEntry(child_obj);
2375 ASSERT(child_entry != NULL);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002376 filler_->SetNamedAutoIndexReference(
2377 HeapGraphEdge::kShortcut,
2378 kInternalRootObject, snapshot_->root(),
2379 child_obj, child_entry);
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002380}
2381
2382
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002383void V8HeapExplorer::SetGcRootsReference(Object* child_obj) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002384 HeapEntry* child_entry = GetEntry(child_obj);
2385 if (child_entry != NULL) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002386 filler_->SetIndexedAutoIndexReference(
2387 HeapGraphEdge::kElement,
2388 kGcRootsObject, snapshot_->gc_roots(),
2389 child_obj, child_entry);
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002390 }
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002391}
2392
2393
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002394void V8HeapExplorer::TagObject(Object* obj, const char* tag) {
2395 if (obj->IsHeapObject() &&
2396 !obj->IsOddball() &&
2397 obj != heap_->raw_unchecked_empty_byte_array() &&
2398 obj != heap_->raw_unchecked_empty_fixed_array() &&
2399 obj != heap_->raw_unchecked_empty_fixed_double_array() &&
2400 obj != heap_->raw_unchecked_empty_descriptor_array()) {
2401 objects_tags_.SetTag(obj, tag);
2402 }
2403}
2404
2405
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002406class GlobalObjectsEnumerator : public ObjectVisitor {
2407 public:
2408 virtual void VisitPointers(Object** start, Object** end) {
2409 for (Object** p = start; p < end; p++) {
2410 if ((*p)->IsGlobalContext()) {
2411 Context* context = Context::cast(*p);
2412 JSObject* proxy = context->global_proxy();
2413 if (proxy->IsJSGlobalProxy()) {
2414 Object* global = proxy->map()->prototype();
2415 if (global->IsJSGlobalObject()) {
2416 objects_.Add(Handle<JSGlobalObject>(JSGlobalObject::cast(global)));
2417 }
2418 }
2419 }
2420 }
2421 }
2422 int count() { return objects_.length(); }
2423 Handle<JSGlobalObject>& at(int i) { return objects_[i]; }
2424
2425 private:
2426 List<Handle<JSGlobalObject> > objects_;
2427};
2428
2429
2430// Modifies heap. Must not be run during heap traversal.
2431void V8HeapExplorer::TagGlobalObjects() {
2432 Isolate* isolate = Isolate::Current();
2433 GlobalObjectsEnumerator enumerator;
2434 isolate->global_handles()->IterateAllRoots(&enumerator);
2435 Handle<String> document_string =
2436 isolate->factory()->NewStringFromAscii(CStrVector("document"));
2437 Handle<String> url_string =
2438 isolate->factory()->NewStringFromAscii(CStrVector("URL"));
2439 const char** urls = NewArray<const char*>(enumerator.count());
2440 for (int i = 0, l = enumerator.count(); i < l; ++i) {
2441 urls[i] = NULL;
2442 Handle<JSGlobalObject> global_obj = enumerator.at(i);
2443 Object* obj_document;
2444 if (global_obj->GetProperty(*document_string)->ToObject(&obj_document) &&
2445 obj_document->IsJSObject()) {
2446 JSObject* document = JSObject::cast(obj_document);
2447 Object* obj_url;
2448 if (document->GetProperty(*url_string)->ToObject(&obj_url) &&
2449 obj_url->IsString()) {
2450 urls[i] = collection_->names()->GetName(String::cast(obj_url));
2451 }
2452 }
2453 }
2454
2455 AssertNoAllocation no_allocation;
2456 for (int i = 0, l = enumerator.count(); i < l; ++i) {
2457 objects_tags_.SetTag(*enumerator.at(i), urls[i]);
2458 }
2459
2460 DeleteArray(urls);
2461}
2462
2463
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002464class GlobalHandlesExtractor : public ObjectVisitor {
2465 public:
2466 explicit GlobalHandlesExtractor(NativeObjectsExplorer* explorer)
2467 : explorer_(explorer) {}
2468 virtual ~GlobalHandlesExtractor() {}
2469 virtual void VisitPointers(Object** start, Object** end) {
2470 UNREACHABLE();
2471 }
2472 virtual void VisitEmbedderReference(Object** p, uint16_t class_id) {
2473 explorer_->VisitSubtreeWrapper(p, class_id);
2474 }
2475 private:
2476 NativeObjectsExplorer* explorer_;
2477};
2478
2479HeapThing const NativeObjectsExplorer::kNativesRootObject =
2480 reinterpret_cast<HeapThing>(
2481 static_cast<intptr_t>(HeapObjectsMap::kNativesRootObjectId));
2482
2483
2484NativeObjectsExplorer::NativeObjectsExplorer(
2485 HeapSnapshot* snapshot, SnapshottingProgressReportingInterface* progress)
2486 : snapshot_(snapshot),
2487 collection_(snapshot_->collection()),
2488 progress_(progress),
2489 embedder_queried_(false),
2490 objects_by_info_(RetainedInfosMatch),
2491 filler_(NULL) {
2492}
2493
2494
2495NativeObjectsExplorer::~NativeObjectsExplorer() {
2496 for (HashMap::Entry* p = objects_by_info_.Start();
2497 p != NULL;
2498 p = objects_by_info_.Next(p)) {
2499 v8::RetainedObjectInfo* info =
2500 reinterpret_cast<v8::RetainedObjectInfo*>(p->key);
2501 info->Dispose();
2502 List<HeapObject*>* objects =
2503 reinterpret_cast<List<HeapObject*>* >(p->value);
2504 delete objects;
2505 }
2506}
2507
2508
2509HeapEntry* NativeObjectsExplorer::AllocateEntry(
2510 HeapThing ptr, int children_count, int retainers_count) {
2511 if (ptr == kNativesRootObject) {
2512 return snapshot_->AddNativesRootEntry(children_count, retainers_count);
2513 } else {
2514 v8::RetainedObjectInfo* info =
2515 reinterpret_cast<v8::RetainedObjectInfo*>(ptr);
2516 intptr_t elements = info->GetElementCount();
2517 intptr_t size = info->GetSizeInBytes();
2518 return snapshot_->AddEntry(
2519 HeapEntry::kNative,
2520 elements != -1 ?
2521 collection_->names()->GetFormatted(
2522 "%s / %" V8_PTR_PREFIX "d entries",
2523 info->GetLabel(),
2524 info->GetElementCount()) :
2525 collection_->names()->GetCopy(info->GetLabel()),
2526 HeapObjectsMap::GenerateId(info),
2527 size != -1 ? static_cast<int>(size) : 0,
2528 children_count,
2529 retainers_count);
2530 }
2531}
2532
2533
2534void NativeObjectsExplorer::AddRootEntries(SnapshotFillerInterface* filler) {
2535 if (EstimateObjectsCount() <= 0) return;
2536 filler->AddEntry(kNativesRootObject, this);
2537}
2538
2539
2540int NativeObjectsExplorer::EstimateObjectsCount() {
2541 FillRetainedObjects();
2542 return objects_by_info_.occupancy();
2543}
2544
2545
2546void NativeObjectsExplorer::FillRetainedObjects() {
2547 if (embedder_queried_) return;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002548 Isolate* isolate = Isolate::Current();
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002549 // Record objects that are joined into ObjectGroups.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002550 isolate->heap()->CallGlobalGCPrologueCallback();
2551 List<ObjectGroup*>* groups = isolate->global_handles()->object_groups();
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002552 for (int i = 0; i < groups->length(); ++i) {
2553 ObjectGroup* group = groups->at(i);
2554 if (group->info_ == NULL) continue;
2555 List<HeapObject*>* list = GetListMaybeDisposeInfo(group->info_);
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00002556 for (size_t j = 0; j < group->length_; ++j) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002557 HeapObject* obj = HeapObject::cast(*group->objects_[j]);
2558 list->Add(obj);
2559 in_groups_.Insert(obj);
2560 }
2561 group->info_ = NULL; // Acquire info object ownership.
2562 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002563 isolate->global_handles()->RemoveObjectGroups();
2564 isolate->heap()->CallGlobalGCEpilogueCallback();
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002565 // Record objects that are not in ObjectGroups, but have class ID.
2566 GlobalHandlesExtractor extractor(this);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002567 isolate->global_handles()->IterateAllRootsWithClassIds(&extractor);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002568 embedder_queried_ = true;
2569}
2570
2571
2572List<HeapObject*>* NativeObjectsExplorer::GetListMaybeDisposeInfo(
2573 v8::RetainedObjectInfo* info) {
2574 HashMap::Entry* entry =
2575 objects_by_info_.Lookup(info, InfoHash(info), true);
2576 if (entry->value != NULL) {
2577 info->Dispose();
2578 } else {
2579 entry->value = new List<HeapObject*>(4);
2580 }
2581 return reinterpret_cast<List<HeapObject*>* >(entry->value);
2582}
2583
2584
2585bool NativeObjectsExplorer::IterateAndExtractReferences(
2586 SnapshotFillerInterface* filler) {
2587 if (EstimateObjectsCount() <= 0) return true;
2588 filler_ = filler;
2589 FillRetainedObjects();
2590 for (HashMap::Entry* p = objects_by_info_.Start();
2591 p != NULL;
2592 p = objects_by_info_.Next(p)) {
2593 v8::RetainedObjectInfo* info =
2594 reinterpret_cast<v8::RetainedObjectInfo*>(p->key);
2595 SetNativeRootReference(info);
2596 List<HeapObject*>* objects =
2597 reinterpret_cast<List<HeapObject*>* >(p->value);
2598 for (int i = 0; i < objects->length(); ++i) {
2599 SetWrapperNativeReferences(objects->at(i), info);
2600 }
2601 }
2602 SetRootNativesRootReference();
2603 filler_ = NULL;
2604 return true;
2605}
2606
2607
2608void NativeObjectsExplorer::SetNativeRootReference(
2609 v8::RetainedObjectInfo* info) {
2610 HeapEntry* child_entry = filler_->FindOrAddEntry(info, this);
2611 ASSERT(child_entry != NULL);
2612 filler_->SetIndexedAutoIndexReference(
2613 HeapGraphEdge::kElement,
2614 kNativesRootObject, snapshot_->natives_root(),
2615 info, child_entry);
2616}
2617
2618
2619void NativeObjectsExplorer::SetWrapperNativeReferences(
2620 HeapObject* wrapper, v8::RetainedObjectInfo* info) {
2621 HeapEntry* wrapper_entry = filler_->FindEntry(wrapper);
2622 ASSERT(wrapper_entry != NULL);
2623 HeapEntry* info_entry = filler_->FindOrAddEntry(info, this);
2624 ASSERT(info_entry != NULL);
2625 filler_->SetNamedReference(HeapGraphEdge::kInternal,
2626 wrapper, wrapper_entry,
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002627 "native",
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002628 info, info_entry);
2629 filler_->SetIndexedAutoIndexReference(HeapGraphEdge::kElement,
2630 info, info_entry,
2631 wrapper, wrapper_entry);
2632}
2633
2634
2635void NativeObjectsExplorer::SetRootNativesRootReference() {
2636 filler_->SetIndexedAutoIndexReference(
2637 HeapGraphEdge::kElement,
2638 V8HeapExplorer::kInternalRootObject, snapshot_->root(),
2639 kNativesRootObject, snapshot_->natives_root());
2640}
2641
2642
2643void NativeObjectsExplorer::VisitSubtreeWrapper(Object** p, uint16_t class_id) {
2644 if (in_groups_.Contains(*p)) return;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002645 Isolate* isolate = Isolate::Current();
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002646 v8::RetainedObjectInfo* info =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002647 isolate->heap_profiler()->ExecuteWrapperClassCallback(class_id, p);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002648 if (info == NULL) return;
2649 GetListMaybeDisposeInfo(info)->Add(HeapObject::cast(*p));
2650}
2651
2652
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002653HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot,
2654 v8::ActivityControl* control)
2655 : snapshot_(snapshot),
2656 control_(control),
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002657 v8_heap_explorer_(snapshot_, this),
2658 dom_explorer_(snapshot_, this) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002659}
2660
2661
2662class SnapshotCounter : public SnapshotFillerInterface {
2663 public:
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002664 explicit SnapshotCounter(HeapEntriesMap* entries) : entries_(entries) { }
2665 HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
2666 entries_->Pair(ptr, allocator, HeapEntriesMap::kHeapEntryPlaceholder);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002667 return HeapEntriesMap::kHeapEntryPlaceholder;
2668 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002669 HeapEntry* FindEntry(HeapThing ptr) {
2670 return entries_->Map(ptr);
2671 }
2672 HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
2673 HeapEntry* entry = FindEntry(ptr);
2674 return entry != NULL ? entry : AddEntry(ptr, allocator);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002675 }
2676 void SetIndexedReference(HeapGraphEdge::Type,
2677 HeapThing parent_ptr,
2678 HeapEntry*,
2679 int,
2680 HeapThing child_ptr,
2681 HeapEntry*) {
2682 entries_->CountReference(parent_ptr, child_ptr);
2683 }
2684 void SetIndexedAutoIndexReference(HeapGraphEdge::Type,
2685 HeapThing parent_ptr,
2686 HeapEntry*,
2687 HeapThing child_ptr,
2688 HeapEntry*) {
2689 entries_->CountReference(parent_ptr, child_ptr);
2690 }
2691 void SetNamedReference(HeapGraphEdge::Type,
2692 HeapThing parent_ptr,
2693 HeapEntry*,
2694 const char*,
2695 HeapThing child_ptr,
2696 HeapEntry*) {
2697 entries_->CountReference(parent_ptr, child_ptr);
2698 }
2699 void SetNamedAutoIndexReference(HeapGraphEdge::Type,
2700 HeapThing parent_ptr,
2701 HeapEntry*,
2702 HeapThing child_ptr,
2703 HeapEntry*) {
2704 entries_->CountReference(parent_ptr, child_ptr);
2705 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002706
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002707 private:
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002708 HeapEntriesMap* entries_;
2709};
2710
2711
2712class SnapshotFiller : public SnapshotFillerInterface {
2713 public:
2714 explicit SnapshotFiller(HeapSnapshot* snapshot, HeapEntriesMap* entries)
2715 : snapshot_(snapshot),
2716 collection_(snapshot->collection()),
2717 entries_(entries) { }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002718 HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002719 UNREACHABLE();
2720 return NULL;
2721 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002722 HeapEntry* FindEntry(HeapThing ptr) {
2723 return entries_->Map(ptr);
2724 }
2725 HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
2726 HeapEntry* entry = FindEntry(ptr);
2727 return entry != NULL ? entry : AddEntry(ptr, allocator);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002728 }
2729 void SetIndexedReference(HeapGraphEdge::Type type,
2730 HeapThing parent_ptr,
2731 HeapEntry* parent_entry,
2732 int index,
2733 HeapThing child_ptr,
2734 HeapEntry* child_entry) {
2735 int child_index, retainer_index;
2736 entries_->CountReference(
2737 parent_ptr, child_ptr, &child_index, &retainer_index);
2738 parent_entry->SetIndexedReference(
2739 type, child_index, index, child_entry, retainer_index);
2740 }
2741 void SetIndexedAutoIndexReference(HeapGraphEdge::Type type,
2742 HeapThing parent_ptr,
2743 HeapEntry* parent_entry,
2744 HeapThing child_ptr,
2745 HeapEntry* child_entry) {
2746 int child_index, retainer_index;
2747 entries_->CountReference(
2748 parent_ptr, child_ptr, &child_index, &retainer_index);
2749 parent_entry->SetIndexedReference(
2750 type, child_index, child_index + 1, child_entry, retainer_index);
2751 }
2752 void SetNamedReference(HeapGraphEdge::Type type,
2753 HeapThing parent_ptr,
2754 HeapEntry* parent_entry,
2755 const char* reference_name,
2756 HeapThing child_ptr,
2757 HeapEntry* child_entry) {
2758 int child_index, retainer_index;
2759 entries_->CountReference(
2760 parent_ptr, child_ptr, &child_index, &retainer_index);
2761 parent_entry->SetNamedReference(
2762 type, child_index, reference_name, child_entry, retainer_index);
2763 }
2764 void SetNamedAutoIndexReference(HeapGraphEdge::Type type,
2765 HeapThing parent_ptr,
2766 HeapEntry* parent_entry,
2767 HeapThing child_ptr,
2768 HeapEntry* child_entry) {
2769 int child_index, retainer_index;
2770 entries_->CountReference(
2771 parent_ptr, child_ptr, &child_index, &retainer_index);
2772 parent_entry->SetNamedReference(type,
2773 child_index,
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002774 collection_->names()->GetName(child_index + 1),
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002775 child_entry,
2776 retainer_index);
2777 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002778
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002779 private:
2780 HeapSnapshot* snapshot_;
2781 HeapSnapshotsCollection* collection_;
2782 HeapEntriesMap* entries_;
2783};
2784
2785
2786bool HeapSnapshotGenerator::GenerateSnapshot() {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002787 v8_heap_explorer_.TagGlobalObjects();
2788
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002789 // TODO(1562) Profiler assumes that any object that is in the heap after
2790 // full GC is reachable from the root when computing dominators.
2791 // This is not true for weakly reachable objects.
2792 // As a temporary solution we call GC twice.
2793 Isolate::Current()->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask);
2794 Isolate::Current()->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask);
2795
2796#ifdef DEBUG
2797 Heap* debug_heap = Isolate::Current()->heap();
2798 ASSERT(!debug_heap->old_data_space()->was_swept_conservatively());
2799 ASSERT(!debug_heap->old_pointer_space()->was_swept_conservatively());
2800 ASSERT(!debug_heap->code_space()->was_swept_conservatively());
2801 ASSERT(!debug_heap->cell_space()->was_swept_conservatively());
2802 ASSERT(!debug_heap->map_space()->was_swept_conservatively());
2803#endif
2804
2805 // The following code uses heap iterators, so we want the heap to be
2806 // stable. It should follow TagGlobalObjects as that can allocate.
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002807 AssertNoAllocation no_alloc;
2808
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002809#ifdef DEBUG
2810 debug_heap->Verify();
2811#endif
2812
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002813 SetProgressTotal(4); // 2 passes + dominators + sizes.
2814
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002815#ifdef DEBUG
2816 debug_heap->Verify();
2817#endif
2818
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002819 // Pass 1. Iterate heap contents to count entries and references.
2820 if (!CountEntriesAndReferences()) return false;
2821
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002822#ifdef DEBUG
2823 debug_heap->Verify();
2824#endif
2825
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002826 // Allocate and fill entries in the snapshot, allocate references.
2827 snapshot_->AllocateEntries(entries_.entries_count(),
2828 entries_.total_children_count(),
2829 entries_.total_retainers_count());
2830 entries_.AllocateEntries();
2831
2832 // Pass 2. Fill references.
2833 if (!FillReferences()) return false;
2834
2835 if (!SetEntriesDominators()) return false;
2836 if (!ApproximateRetainedSizes()) return false;
2837
2838 progress_counter_ = progress_total_;
2839 if (!ProgressReport(true)) return false;
2840 return true;
2841}
2842
2843
2844void HeapSnapshotGenerator::ProgressStep() {
2845 ++progress_counter_;
2846}
2847
2848
2849bool HeapSnapshotGenerator::ProgressReport(bool force) {
2850 const int kProgressReportGranularity = 10000;
2851 if (control_ != NULL
2852 && (force || progress_counter_ % kProgressReportGranularity == 0)) {
2853 return
2854 control_->ReportProgressValue(progress_counter_, progress_total_) ==
2855 v8::ActivityControl::kContinue;
2856 }
2857 return true;
2858}
2859
2860
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002861void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) {
2862 if (control_ == NULL) return;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002863 HeapIterator iterator(HeapIterator::kFilterUnreachable);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002864 progress_total_ = (
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002865 v8_heap_explorer_.EstimateObjectsCount(&iterator) +
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002866 dom_explorer_.EstimateObjectsCount()) * iterations_count;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002867 progress_counter_ = 0;
2868}
2869
2870
2871bool HeapSnapshotGenerator::CountEntriesAndReferences() {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002872 SnapshotCounter counter(&entries_);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002873 v8_heap_explorer_.AddRootEntries(&counter);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002874 dom_explorer_.AddRootEntries(&counter);
2875 return
2876 v8_heap_explorer_.IterateAndExtractReferences(&counter) &&
2877 dom_explorer_.IterateAndExtractReferences(&counter);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002878}
2879
2880
2881bool HeapSnapshotGenerator::FillReferences() {
2882 SnapshotFiller filler(snapshot_, &entries_);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002883 return
2884 v8_heap_explorer_.IterateAndExtractReferences(&filler) &&
2885 dom_explorer_.IterateAndExtractReferences(&filler);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002886}
2887
2888
2889void HeapSnapshotGenerator::FillReversePostorderIndexes(
2890 Vector<HeapEntry*>* entries) {
2891 snapshot_->ClearPaint();
2892 int current_entry = 0;
2893 List<HeapEntry*> nodes_to_visit;
2894 nodes_to_visit.Add(snapshot_->root());
2895 snapshot_->root()->paint_reachable();
2896 while (!nodes_to_visit.is_empty()) {
2897 HeapEntry* entry = nodes_to_visit.last();
2898 Vector<HeapGraphEdge> children = entry->children();
2899 bool has_new_edges = false;
2900 for (int i = 0; i < children.length(); ++i) {
2901 if (children[i].type() == HeapGraphEdge::kShortcut) continue;
2902 HeapEntry* child = children[i].to();
2903 if (!child->painted_reachable()) {
2904 nodes_to_visit.Add(child);
2905 child->paint_reachable();
2906 has_new_edges = true;
2907 }
2908 }
2909 if (!has_new_edges) {
2910 entry->set_ordered_index(current_entry);
2911 (*entries)[current_entry++] = entry;
2912 nodes_to_visit.RemoveLast();
2913 }
2914 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002915 ASSERT_EQ(current_entry, entries->length());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002916}
2917
2918
2919static int Intersect(int i1, int i2, const Vector<HeapEntry*>& dominators) {
2920 int finger1 = i1, finger2 = i2;
2921 while (finger1 != finger2) {
2922 while (finger1 < finger2) finger1 = dominators[finger1]->ordered_index();
2923 while (finger2 < finger1) finger2 = dominators[finger2]->ordered_index();
2924 }
2925 return finger1;
2926}
2927
2928// The algorithm is based on the article:
2929// K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm"
sgjesse@chromium.org496c03a2011-02-14 12:05:43 +00002930// Softw. Pract. Exper. 4 (2001), pp. 1-10.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002931bool HeapSnapshotGenerator::BuildDominatorTree(
2932 const Vector<HeapEntry*>& entries,
2933 Vector<HeapEntry*>* dominators) {
2934 if (entries.length() == 0) return true;
2935 const int entries_length = entries.length(), root_index = entries_length - 1;
2936 for (int i = 0; i < root_index; ++i) (*dominators)[i] = NULL;
2937 (*dominators)[root_index] = entries[root_index];
2938 int changed = 1;
2939 const int base_progress_counter = progress_counter_;
2940 while (changed != 0) {
2941 changed = 0;
2942 for (int i = root_index - 1; i >= 0; --i) {
2943 HeapEntry* new_idom = NULL;
2944 Vector<HeapGraphEdge*> rets = entries[i]->retainers();
2945 int j = 0;
2946 for (; j < rets.length(); ++j) {
2947 if (rets[j]->type() == HeapGraphEdge::kShortcut) continue;
2948 HeapEntry* ret = rets[j]->From();
2949 if (dominators->at(ret->ordered_index()) != NULL) {
2950 new_idom = ret;
2951 break;
2952 }
2953 }
2954 for (++j; j < rets.length(); ++j) {
2955 if (rets[j]->type() == HeapGraphEdge::kShortcut) continue;
2956 HeapEntry* ret = rets[j]->From();
2957 if (dominators->at(ret->ordered_index()) != NULL) {
2958 new_idom = entries[Intersect(ret->ordered_index(),
2959 new_idom->ordered_index(),
2960 *dominators)];
2961 }
2962 }
2963 if (new_idom != NULL && dominators->at(i) != new_idom) {
2964 (*dominators)[i] = new_idom;
2965 ++changed;
2966 }
2967 }
2968 int remaining = entries_length - changed;
2969 if (remaining < 0) remaining = 0;
2970 progress_counter_ = base_progress_counter + remaining;
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002971 if (!ProgressReport(true)) return false;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002972 }
2973 return true;
2974}
2975
2976
2977bool HeapSnapshotGenerator::SetEntriesDominators() {
2978 // This array is used for maintaining reverse postorder of nodes.
2979 ScopedVector<HeapEntry*> ordered_entries(snapshot_->entries()->length());
2980 FillReversePostorderIndexes(&ordered_entries);
2981 ScopedVector<HeapEntry*> dominators(ordered_entries.length());
2982 if (!BuildDominatorTree(ordered_entries, &dominators)) return false;
2983 for (int i = 0; i < ordered_entries.length(); ++i) {
2984 ASSERT(dominators[i] != NULL);
2985 ordered_entries[i]->set_dominator(dominators[i]);
2986 }
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00002987 return true;
2988}
2989
2990
2991bool HeapSnapshotGenerator::ApproximateRetainedSizes() {
2992 // As for the dominators tree we only know parent nodes, not
2993 // children, to sum up total sizes we "bubble" node's self size
2994 // adding it to all of its parents.
2995 for (int i = 0; i < snapshot_->entries()->length(); ++i) {
2996 HeapEntry* entry = snapshot_->entries()->at(i);
2997 entry->set_retained_size(entry->self_size());
2998 }
2999 for (int i = 0;
3000 i < snapshot_->entries()->length();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003001 ++i, ProgressStep()) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003002 HeapEntry* entry = snapshot_->entries()->at(i);
3003 int entry_size = entry->self_size();
3004 for (HeapEntry* dominator = entry->dominator();
3005 dominator != entry;
3006 entry = dominator, dominator = entry->dominator()) {
3007 dominator->add_retained_size(entry_size);
3008 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003009 if (!ProgressReport()) return false;
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003010 }
3011 return true;
3012}
3013
3014
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003015class OutputStreamWriter {
3016 public:
3017 explicit OutputStreamWriter(v8::OutputStream* stream)
3018 : stream_(stream),
3019 chunk_size_(stream->GetChunkSize()),
3020 chunk_(chunk_size_),
3021 chunk_pos_(0),
3022 aborted_(false) {
3023 ASSERT(chunk_size_ > 0);
3024 }
3025 bool aborted() { return aborted_; }
3026 void AddCharacter(char c) {
3027 ASSERT(c != '\0');
3028 ASSERT(chunk_pos_ < chunk_size_);
3029 chunk_[chunk_pos_++] = c;
3030 MaybeWriteChunk();
3031 }
3032 void AddString(const char* s) {
3033 AddSubstring(s, StrLength(s));
3034 }
3035 void AddSubstring(const char* s, int n) {
3036 if (n <= 0) return;
3037 ASSERT(static_cast<size_t>(n) <= strlen(s));
3038 const char* s_end = s + n;
3039 while (s < s_end) {
3040 int s_chunk_size = Min(
3041 chunk_size_ - chunk_pos_, static_cast<int>(s_end - s));
3042 ASSERT(s_chunk_size > 0);
3043 memcpy(chunk_.start() + chunk_pos_, s, s_chunk_size);
3044 s += s_chunk_size;
3045 chunk_pos_ += s_chunk_size;
3046 MaybeWriteChunk();
3047 }
3048 }
3049 void AddNumber(int n) { AddNumberImpl<int>(n, "%d"); }
3050 void AddNumber(unsigned n) { AddNumberImpl<unsigned>(n, "%u"); }
3051 void AddNumber(uint64_t n) { AddNumberImpl<uint64_t>(n, "%llu"); }
3052 void Finalize() {
3053 if (aborted_) return;
3054 ASSERT(chunk_pos_ < chunk_size_);
3055 if (chunk_pos_ != 0) {
3056 WriteChunk();
3057 }
3058 stream_->EndOfStream();
3059 }
3060
3061 private:
3062 template<typename T>
3063 void AddNumberImpl(T n, const char* format) {
3064 ScopedVector<char> buffer(32);
3065 int result = OS::SNPrintF(buffer, format, n);
3066 USE(result);
3067 ASSERT(result != -1);
3068 AddString(buffer.start());
3069 }
3070 void MaybeWriteChunk() {
3071 ASSERT(chunk_pos_ <= chunk_size_);
3072 if (chunk_pos_ == chunk_size_) {
3073 WriteChunk();
3074 chunk_pos_ = 0;
3075 }
3076 }
3077 void WriteChunk() {
3078 if (aborted_) return;
3079 if (stream_->WriteAsciiChunk(chunk_.start(), chunk_pos_) ==
3080 v8::OutputStream::kAbort) aborted_ = true;
3081 }
3082
3083 v8::OutputStream* stream_;
3084 int chunk_size_;
3085 ScopedVector<char> chunk_;
3086 int chunk_pos_;
3087 bool aborted_;
3088};
3089
fschneider@chromium.org1805e212011-09-05 10:49:12 +00003090const int HeapSnapshotJSONSerializer::kMaxSerializableSnapshotRawSize =
3091 256 * MB;
3092
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003093void HeapSnapshotJSONSerializer::Serialize(v8::OutputStream* stream) {
3094 ASSERT(writer_ == NULL);
3095 writer_ = new OutputStreamWriter(stream);
3096
fschneider@chromium.org1805e212011-09-05 10:49:12 +00003097 HeapSnapshot* original_snapshot = NULL;
3098 if (snapshot_->raw_entries_size() >= kMaxSerializableSnapshotRawSize) {
3099 // The snapshot is too big. Serialize a fake snapshot.
3100 original_snapshot = snapshot_;
3101 snapshot_ = CreateFakeSnapshot();
3102 }
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003103 // Since nodes graph is cyclic, we need the first pass to enumerate
3104 // them. Strings can be serialized in one pass.
3105 EnumerateNodes();
3106 SerializeImpl();
3107
3108 delete writer_;
3109 writer_ = NULL;
fschneider@chromium.org1805e212011-09-05 10:49:12 +00003110
3111 if (original_snapshot != NULL) {
3112 delete snapshot_;
3113 snapshot_ = original_snapshot;
3114 }
3115}
3116
3117
3118HeapSnapshot* HeapSnapshotJSONSerializer::CreateFakeSnapshot() {
3119 HeapSnapshot* result = new HeapSnapshot(snapshot_->collection(),
3120 HeapSnapshot::kFull,
3121 snapshot_->title(),
3122 snapshot_->uid());
3123 result->AllocateEntries(2, 1, 0);
3124 HeapEntry* root = result->AddRootEntry(1);
3125 HeapEntry* message = result->AddEntry(
3126 HeapEntry::kString, "The snapshot is too big", 0, 4, 0, 0);
3127 root->SetUnidirElementReference(0, 1, message);
3128 result->SetDominatorsToSelf();
3129 return result;
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003130}
3131
3132
3133void HeapSnapshotJSONSerializer::SerializeImpl() {
3134 writer_->AddCharacter('{');
3135 writer_->AddString("\"snapshot\":{");
3136 SerializeSnapshot();
3137 if (writer_->aborted()) return;
3138 writer_->AddString("},\n");
3139 writer_->AddString("\"nodes\":[");
3140 SerializeNodes();
3141 if (writer_->aborted()) return;
3142 writer_->AddString("],\n");
3143 writer_->AddString("\"strings\":[");
3144 SerializeStrings();
3145 if (writer_->aborted()) return;
3146 writer_->AddCharacter(']');
3147 writer_->AddCharacter('}');
3148 writer_->Finalize();
3149}
3150
3151
3152class HeapSnapshotJSONSerializerEnumerator {
3153 public:
3154 explicit HeapSnapshotJSONSerializerEnumerator(HeapSnapshotJSONSerializer* s)
3155 : s_(s) {
3156 }
3157 void Apply(HeapEntry** entry) {
3158 s_->GetNodeId(*entry);
3159 }
3160 private:
3161 HeapSnapshotJSONSerializer* s_;
3162};
3163
3164void HeapSnapshotJSONSerializer::EnumerateNodes() {
3165 GetNodeId(snapshot_->root()); // Make sure root gets the first id.
3166 HeapSnapshotJSONSerializerEnumerator iter(this);
3167 snapshot_->IterateEntries(&iter);
3168}
3169
3170
3171int HeapSnapshotJSONSerializer::GetNodeId(HeapEntry* entry) {
3172 HashMap::Entry* cache_entry = nodes_.Lookup(entry, ObjectHash(entry), true);
3173 if (cache_entry->value == NULL) {
3174 cache_entry->value = reinterpret_cast<void*>(next_node_id_++);
3175 }
3176 return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
3177}
3178
3179
3180int HeapSnapshotJSONSerializer::GetStringId(const char* s) {
3181 HashMap::Entry* cache_entry = strings_.Lookup(
3182 const_cast<char*>(s), ObjectHash(s), true);
3183 if (cache_entry->value == NULL) {
3184 cache_entry->value = reinterpret_cast<void*>(next_string_id_++);
3185 }
3186 return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
3187}
3188
3189
3190void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge) {
3191 writer_->AddCharacter(',');
3192 writer_->AddNumber(edge->type());
3193 writer_->AddCharacter(',');
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00003194 if (edge->type() == HeapGraphEdge::kElement
3195 || edge->type() == HeapGraphEdge::kHidden) {
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003196 writer_->AddNumber(edge->index());
3197 } else {
3198 writer_->AddNumber(GetStringId(edge->name()));
3199 }
3200 writer_->AddCharacter(',');
3201 writer_->AddNumber(GetNodeId(edge->to()));
3202}
3203
3204
3205void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) {
3206 writer_->AddCharacter('\n');
3207 writer_->AddCharacter(',');
3208 writer_->AddNumber(entry->type());
3209 writer_->AddCharacter(',');
3210 writer_->AddNumber(GetStringId(entry->name()));
3211 writer_->AddCharacter(',');
3212 writer_->AddNumber(entry->id());
3213 writer_->AddCharacter(',');
3214 writer_->AddNumber(entry->self_size());
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00003215 writer_->AddCharacter(',');
3216 writer_->AddNumber(entry->RetainedSize(false));
3217 writer_->AddCharacter(',');
3218 writer_->AddNumber(GetNodeId(entry->dominator()));
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003219 Vector<HeapGraphEdge> children = entry->children();
3220 writer_->AddCharacter(',');
3221 writer_->AddNumber(children.length());
3222 for (int i = 0; i < children.length(); ++i) {
3223 SerializeEdge(&children[i]);
3224 if (writer_->aborted()) return;
3225 }
3226}
3227
3228
3229void HeapSnapshotJSONSerializer::SerializeNodes() {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00003230 // The first (zero) item of nodes array is an object describing node
3231 // serialization layout. We use a set of macros to improve
3232 // readability.
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003233#define JSON_A(s) "["s"]"
3234#define JSON_O(s) "{"s"}"
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00003235#define JSON_S(s) "\""s"\""
3236 writer_->AddString(JSON_O(
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003237 JSON_S("fields") ":" JSON_A(
3238 JSON_S("type")
3239 "," JSON_S("name")
3240 "," JSON_S("id")
3241 "," JSON_S("self_size")
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00003242 "," JSON_S("retained_size")
3243 "," JSON_S("dominator")
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003244 "," JSON_S("children_count")
3245 "," JSON_S("children"))
3246 "," JSON_S("types") ":" JSON_A(
3247 JSON_A(
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00003248 JSON_S("hidden")
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003249 "," JSON_S("array")
3250 "," JSON_S("string")
3251 "," JSON_S("object")
3252 "," JSON_S("code")
vegorov@chromium.org42841962010-10-18 11:18:59 +00003253 "," JSON_S("closure")
3254 "," JSON_S("regexp")
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00003255 "," JSON_S("number")
3256 "," JSON_S("native"))
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003257 "," JSON_S("string")
3258 "," JSON_S("number")
3259 "," JSON_S("number")
3260 "," JSON_S("number")
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00003261 "," JSON_S("number")
3262 "," JSON_S("number")
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003263 "," JSON_O(
3264 JSON_S("fields") ":" JSON_A(
3265 JSON_S("type")
3266 "," JSON_S("name_or_index")
3267 "," JSON_S("to_node"))
3268 "," JSON_S("types") ":" JSON_A(
3269 JSON_A(
3270 JSON_S("context")
3271 "," JSON_S("element")
3272 "," JSON_S("property")
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00003273 "," JSON_S("internal")
3274 "," JSON_S("hidden")
3275 "," JSON_S("shortcut"))
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003276 "," JSON_S("string_or_number")
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00003277 "," JSON_S("node"))))));
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003278#undef JSON_S
3279#undef JSON_O
3280#undef JSON_A
3281
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00003282 const int node_fields_count = 7;
3283 // type,name,id,self_size,retained_size,dominator,children_count.
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003284 const int edge_fields_count = 3; // type,name|index,to_node.
3285 List<HashMap::Entry*> sorted_nodes;
3286 SortHashMap(&nodes_, &sorted_nodes);
3287 // Rewrite node ids, so they refer to actual array positions.
3288 if (sorted_nodes.length() > 1) {
3289 // Nodes start from array index 1.
3290 int prev_value = 1;
3291 sorted_nodes[0]->value = reinterpret_cast<void*>(prev_value);
3292 for (int i = 1; i < sorted_nodes.length(); ++i) {
3293 HeapEntry* prev_heap_entry =
3294 reinterpret_cast<HeapEntry*>(sorted_nodes[i-1]->key);
3295 prev_value += node_fields_count +
3296 prev_heap_entry->children().length() * edge_fields_count;
3297 sorted_nodes[i]->value = reinterpret_cast<void*>(prev_value);
3298 }
3299 }
3300 for (int i = 0; i < sorted_nodes.length(); ++i) {
3301 SerializeNode(reinterpret_cast<HeapEntry*>(sorted_nodes[i]->key));
3302 if (writer_->aborted()) return;
3303 }
3304}
3305
3306
3307void HeapSnapshotJSONSerializer::SerializeSnapshot() {
3308 writer_->AddString("\"title\":\"");
3309 writer_->AddString(snapshot_->title());
3310 writer_->AddString("\"");
3311 writer_->AddString(",\"uid\":");
3312 writer_->AddNumber(snapshot_->uid());
3313}
3314
3315
3316static void WriteUChar(OutputStreamWriter* w, unibrow::uchar u) {
3317 static const char hex_chars[] = "0123456789ABCDEF";
3318 w->AddString("\\u");
3319 w->AddCharacter(hex_chars[(u >> 12) & 0xf]);
3320 w->AddCharacter(hex_chars[(u >> 8) & 0xf]);
3321 w->AddCharacter(hex_chars[(u >> 4) & 0xf]);
3322 w->AddCharacter(hex_chars[u & 0xf]);
3323}
3324
3325void HeapSnapshotJSONSerializer::SerializeString(const unsigned char* s) {
3326 writer_->AddCharacter('\n');
3327 writer_->AddCharacter('\"');
3328 for ( ; *s != '\0'; ++s) {
3329 switch (*s) {
3330 case '\b':
3331 writer_->AddString("\\b");
3332 continue;
3333 case '\f':
3334 writer_->AddString("\\f");
3335 continue;
3336 case '\n':
3337 writer_->AddString("\\n");
3338 continue;
3339 case '\r':
3340 writer_->AddString("\\r");
3341 continue;
3342 case '\t':
3343 writer_->AddString("\\t");
3344 continue;
3345 case '\"':
3346 case '\\':
3347 writer_->AddCharacter('\\');
3348 writer_->AddCharacter(*s);
3349 continue;
3350 default:
3351 if (*s > 31 && *s < 128) {
3352 writer_->AddCharacter(*s);
3353 } else if (*s <= 31) {
3354 // Special character with no dedicated literal.
3355 WriteUChar(writer_, *s);
3356 } else {
3357 // Convert UTF-8 into \u UTF-16 literal.
3358 unsigned length = 1, cursor = 0;
3359 for ( ; length <= 4 && *(s + length) != '\0'; ++length) { }
3360 unibrow::uchar c = unibrow::Utf8::CalculateValue(s, length, &cursor);
3361 if (c != unibrow::Utf8::kBadChar) {
3362 WriteUChar(writer_, c);
3363 ASSERT(cursor != 0);
3364 s += cursor - 1;
3365 } else {
3366 writer_->AddCharacter('?');
3367 }
3368 }
3369 }
3370 }
3371 writer_->AddCharacter('\"');
3372}
3373
3374
3375void HeapSnapshotJSONSerializer::SerializeStrings() {
3376 List<HashMap::Entry*> sorted_strings;
3377 SortHashMap(&strings_, &sorted_strings);
3378 writer_->AddString("\"<dummy>\"");
3379 for (int i = 0; i < sorted_strings.length(); ++i) {
3380 writer_->AddCharacter(',');
3381 SerializeString(
3382 reinterpret_cast<const unsigned char*>(sorted_strings[i]->key));
3383 if (writer_->aborted()) return;
3384 }
3385}
3386
3387
3388template<typename T>
3389inline static int SortUsingEntryValue(const T* x, const T* y) {
3390 uintptr_t x_uint = reinterpret_cast<uintptr_t>((*x)->value);
3391 uintptr_t y_uint = reinterpret_cast<uintptr_t>((*y)->value);
3392 if (x_uint > y_uint) {
3393 return 1;
3394 } else if (x_uint == y_uint) {
3395 return 0;
3396 } else {
3397 return -1;
3398 }
3399}
3400
3401
3402void HeapSnapshotJSONSerializer::SortHashMap(
3403 HashMap* map, List<HashMap::Entry*>* sorted_entries) {
3404 for (HashMap::Entry* p = map->Start(); p != NULL; p = map->Next(p))
3405 sorted_entries->Add(p);
3406 sorted_entries->Sort(SortUsingEntryValue);
3407}
3408
fschneider@chromium.org086aac62010-03-17 13:18:24 +00003409} } // namespace v8::internal